题目大意:
就是现在对于一个长度不超过10^6的字符串S, 接下来有n次询问(n <= 10^6), 每次询问有一个字符串x, 求x或者x轮换变换之后能在S中找到的对应字串个数, 输入的x的字符串总长度不超过10^6
轮换变化x比如 x = "aaba", 那么对应的要找出"aaba", "abaa", "baaa", "aaab"在S中出现次数的和
大致思路:
后缀自动机一发AC...
首先对于S建立后缀自动机, 然后对于每一个询问的字符串xi, 变成xi+xi的形式, 这样对于xi + xi这个字符串在S的后缀自动机上进行匹配, 当匹配长度到xi的长度时就将匹配长度减少一位, 如果当前匹配长度cnt在状态t上且Min(t) == cnt, 就转移到parent点, cnt--, 如果不是就cnt--, 依旧保持在这个t状态的位置,没到达一个cnt = length(xi)的状态, 这个状态的right集合的大小就是这样一个轮换出现的次数, 这样每次O(length(xi))匹配之后就得到了所有轮换的串出现的次数, 在匹配的时候记录已经计算了的状态不要重复计算就可以了
代码如下:
Result : Accepted Memory : 265180 KB Time : 280 ms
/* * Author: Gatevin * Created Time: 2015/4/11 15:21:05 * File Name: Rin_Tohsaka.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define foreach(e, x) for(__typeof(x.begin()) e = x.begin(); e != x.end(); ++e) #define SHOW_MEMORY(x) cout<<sizeof(x)/(1024*1024.)<<"MB"<<endl #define maxm 1000010 #define maxn 2000010 struct Suffix_Automation { struct State { State *par; State *go[26]; int val, right, mi, cnt, vis; void init(int _val = 0) { par = 0, val = _val, right = mi = cnt = vis = 0; memset(go, 0, sizeof(go)); } int calc() { if(par == 0) return 0; else return val - par->val; } }; State *root, *last, *cur; State nodePool[maxn]; State* newState(int val = 0) { cur->init(val); return cur++; } void initSAM() { cur = nodePool; root = newState(); last = root; } void extend(int w) { State *p = last; State *np = newState(p->val + 1); np->right = 1; while(p && p->go[w] == 0) { p->go[w] = np; p = p->par; } if(p == 0) { np->par = root; } else { State *q = p->go[w]; if(q->val == p->val + 1) { np->par = q; } else { State *nq = newState(p->val + 1); memcpy(nq->go, q->go, sizeof(q->go));//刚开始这句写掉了...调试了一段时间才过样例... nq->par = q->par; q->par = nq; np->par = nq; while(p && p->go[w] == q) { p->go[w] = nq; p = p->par; } } } last = np; } int d[maxm]; State* b[maxn]; void topo() { int maxVal = 0; memset(d, 0, sizeof(d)); int cnt = cur - nodePool; for(int i = 1; i < cnt; i++) maxVal = max(maxVal, nodePool[i].val), d[nodePool[i].val]++; for(int i = 1; i <= maxVal; i++) d[i] += d[i - 1]; for(int i = 1; i < cnt; i++) b[d[nodePool[i].val]--] = &nodePool[i]; b[0] = root; } void SAMInfo() { State *p; int cnt = cur - nodePool; for(int i = cnt - 1; i > 0; i--) { p = b[i]; p->par->right += p->right; p->mi = p->par->val + 1; } } void solve(char *x, int len, int L, int cas) { State *now = root; int cnt = 0; int ans = 0; for(int i = 0; i < L - 1; i++) { int w = x[i] - 'a'; if(now->go[w]) { now = now->go[w]; cnt++; if(cnt == len) { if(now->vis != cas) { ans += now->right; now->vis = cas;//标记这个状态计算过了 } if(len == now->mi)//转移到其Parent点 { now = now->par; cnt = now->val; } else cnt--;//保留在这个点 } } else { while(now && now->go[w] == 0) { now = now->par; if(now) cnt = now->val; } if(now == 0) { now = root; cnt = 0; } else { now = now->go[w]; cnt++; if(cnt == len) { if(now->vis != cas) { ans += now->right; now->vis = cas;//标记计算过了 } if(len == now->mi) { now = now->par; cnt = now->val; } else cnt--; } } } } printf("%d\n", ans); } }; Suffix_Automation sam; char s[maxm]; int n; char x[maxn]; int main() { gets(s); int lens = strlen(s); sam.initSAM(); for(int i = 0; i < lens; i++) sam.extend(s[i] - 'a'); sam.topo(); sam.SAMInfo(); scanf("%d", &n); getchar(); for(int cas = 1; cas <= n; cas++) { gets(x); int len = strlen(x); for(int i = 0; i < len; i++) x[i + len] = x[i]; sam.solve(x, len, 2*len, cas); } return 0; }