题意:给你S串和n个T串,求T串的所有循环串在S中的出现次数。
解法:
SAM对S串建立自动机
将T串更新为双倍的回文串
将最新更新的T串在自动机上扫描(类似,LCS),并标记所取的节点,避免重复串就行再次选取
- /*************************************************************************
- > File Name: 235c.cpp
- > Author: cy
- > Mail: 1002@qq.com
- > Created Time: 14/11/26 19:24:51
- ************************************************************************/
- #include<iostream>
- #include<cstring>
- #include <algorithm>
- #include<cstdlib>
- #include<vector>
- #include<cmath>
- #include<stdlib.h>
- #include<iomanip>
- #include<list>
- #include<deque>
- #include<map>
- #include <stdio.h>
- #include <queue>
- #define inf 0x3f3f3f3f
- #define INF 0x3FFFFFFFFFFFFFFFLL
- #define rep(i,n) for(i=0;i<n;i++)
- #define reP(i,n) for(i=1;i<=n;i++)
- #define ull unsigned long long
- #define ll long long
- #define cle(a) memset(a,0,sizeof(a))
- using namespace std;
- #define MAXN 2000010+5
- #define MAXC 26
- char ch[MAXN];
- int rk[MAXN],sz[MAXN],ans[MAXN];
- int T;
- struct Suffix_Automaton{
- int child[MAXN][MAXC];
- int f[MAXN];//指向最近的接受态
- int num[MAXN];//匹配个数
- int l[MAXN];//此节点代表的最长串的长度
- int head,tail,tot;//头节点,最后的接受态,总节点
- void clear(){
- memset(child,0,sizeof(child));
- memset(f,0,sizeof(f));
- memset(l,0,sizeof(l));
- cle(num);
- head=tail=tot=1;
- }
- void add(int c){
- int p=tail,np=++tot;l[np]=l[p]+1;tail=np;
- for(;p&&!child[p][c];p=f[p]) child[p][c]=np;
- if(!p) f[np]=head;
- else if(l[child[p][c]]==l[p]+1) f[np]=child[p][c];
- else{
- int q=child[p][c],r=++tot;
- memcpy(child[r],child[q],MAXC*sizeof(int));
- f[r]=f[q];l[r]=l[p]+1;f[q]=f[np]=r;
- for(;p&&child[p][c]==q;p=f[p]) child[p][c]=r;
- }
- }
- void insert(char*ch){
- int l=strlen(ch);
- for(int i=0;i<l;i++) add(ch[i]-'a');
- }
- int ws[MAXN],used[MAXN];
- void tpu(){
- int len=strlen(ch);
- int p=head;
- for(int i=0;i<len;i++){
- p=child[p][ch[i]-'a'];
- num[p]=1;//自己本身的前缀串标记
- }
- for(int i=0;i<=len;i++)ws[i]=0;
- for(int i=1;i<=tot;i++)ws[l[i]]++;
- for(int i=1;i<=len;i++)ws[i]+=ws[i-1];
- for(int i=tot;i>0;i--)rk[ws[l[i]]--]=i;
- for(int i=tot;i>0;i--){//沿父节点向上更新
- num[f[rk[i]]]+=num[rk[i]];//个数和
- }
- T=0;cle(used);
- }
- int solve(char *ch)//最长公共子串
- {
- int len=strlen(ch);
- int temp=0;
- int p=head;
- int ans=0;
- int i;
- ++T;//标记被那次使用了
- for(i=0;i<len;i++){
- int ip=ch[i]-'a';
- while(p&&!child[p][ip]){//找到匹配位置
- p=f[p];
- }
- if(!p){
- temp=0;
- p=head;
- }
- else{
- temp=min(temp,l[p])+1;
- p=child[p][ip];
- }
- while(l[f[p]]>=len/2){//退回到匹配长度
- p=f[p];
- temp=l[p];
- }
- if(temp>=len/2&&used[p]!=T)
- {
- used[p]=T;
- ans+=num[p];
- }
- }
- return ans;
- }
- }SAM;
- int main()
- {
- #ifndef ONLINE_JUDGE
- freopen("in.txt","r",stdin);
- //freopen("out.txt","w",stdout);
- #endif
- scanf("%s",&ch);
- SAM.clear();
- SAM.insert(ch);
- SAM.tpu();
- int n;
- cin>>n;
- int i,j;
- rep(j,n)
- {
- scanf("%s",&ch);
- int len=strlen(ch);
- for(i=0;i<len;i++){
- ch[i+len]=ch[i];
- }
- ch[i+len]='\\0';
- printf("%d\\n",SAM.solve(ch));
- }
- return 0;
- }