HDU4622 (查询一段字符串的不同子串个数,后缀自动机)
Posted shuaihui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU4622 (查询一段字符串的不同子串个数,后缀自动机)相关的知识,希望对你有一定的参考价值。
http://acm.hdu.edu.cn/showproblem.php?pid=4622
题意:给出一个字符串和q次询问,每次询问[l,r]区间内不同子串的个数
分析:
N<=2000.
我是用后缀自动机预处理出所有区间的不同子串个数。
建立n次后缀自动机。
为什么要建立N次呢? 因为鸭 , 后缀自动机之所以有继承性是因为定义的起点是相同的 , 而起点不同是没有继承性的 , 所以要枚举n次不同的节点建立后缀自动机。
用一个变量t , 不断的累加建立到当前点有多少个不同的字符串 , 就是前缀和的思想
#include <bits/stdc++.h> #define LL long long #define P pair<int, int> #define lowbit(x) (x & -x) #define mem(a, b) memset(a, b, sizeof(a)) #define rep(i, a, n) for (int i = a; i <= n; ++i) const int maxn = 2005; #define mid ((l + r) >> 1) #define lc rt<<1 #define rc rt<<1|1 using namespace std; struct SAM{ int trans[maxn<<1][26], slink[maxn<<1], maxlen[maxn<<1]; int last, now, root, len; inline void newnode (int v) { maxlen[++now] = v; } inline int extend(int c) { newnode(maxlen[last] + 1); int p = last, np = now; // 更新trans while (p && !trans[p][c]) { trans[p][c] = np; p = slink[p]; } if (!p) slink[np] = root; else { int q = trans[p][c]; if (maxlen[p] + 1 != maxlen[q]) { // 将q点拆出nq,使得maxlen[p] + 1 == maxlen[q] newnode(maxlen[p] + 1); int nq = now; memcpy(trans[nq], trans[q], sizeof(trans[q])); slink[nq] = slink[q]; slink[q] = slink[np] = nq; while (p!=-1 && trans[p][c] == q) { trans[p][c] = nq; p = slink[p]; } }else slink[np] = q; } last = np; // 初始状态为可接受状态 return maxlen[np]-maxlen[slink[np]]; } inline void init() { memset(trans,0,sizeof(trans)); memset(slink,0,sizeof(slink)); memset(maxlen,0,sizeof(maxlen)); root = last=now=1; } }sam; int ans[maxn][maxn]; int main() { int t;scanf("%d",&t); while(t--) { string str;cin>>str; for(int i=0 ; i<str.size() ; i++) { sam.init(); int T=0; for(int j=i ; j<str.size() ; j++) { T+=sam.extend(str[j]-‘a‘); ans[i+1][j+1]=T; } } int q;scanf("%d",&q); while(q--) { int u,v;scanf("%d%d",&u,&v); printf("%d ",ans[u][v]); } } //- sam.all(); }
以上是关于HDU4622 (查询一段字符串的不同子串个数,后缀自动机)的主要内容,如果未能解决你的问题,请参考以下文章