CF 235C

题意:给你S串和n个T串,求T串的所有循环串在S中的出现次数。

题目链接

解法:

  • SAM对S串建立自动机

  • 将T串更新为双倍的回文串

  • 将最新更新的T串在自动机上扫描(类似,LCS),并标记所取的节点,避免重复串就行再次选取

 
 
  1. /************************************************************************* 
  2.     > File Name: 235c.cpp 
  3.     > Author: cy 
  4.     > Mail: 1002@qq.com  
  5.     > Created Time: 14/11/26 19:24:51 
  6.  ************************************************************************/ 
  7.  
  8. #include<iostream> 
  9. #include<cstring> 
  10. #include <algorithm> 
  11. #include<cstdlib> 
  12. #include<vector> 
  13. #include<cmath> 
  14. #include<stdlib.h> 
  15. #include<iomanip> 
  16. #include<list> 
  17. #include<deque> 
  18. #include<map> 
  19. #include <stdio.h> 
  20. #include <queue> 
  21.  
  22.  
  23. #define inf 0x3f3f3f3f 
  24.   #define INF 0x3FFFFFFFFFFFFFFFLL 
  25. #define rep(i,n) for(i=0;i<n;i++) 
  26.  #define reP(i,n) for(i=1;i<=n;i++) 
  27.  
  28. #define ull unsigned long long 
  29.  #define ll long long 
  30.  
  31. #define cle(a) memset(a,0,sizeof(a)) 
  32.  
  33. using namespace std; 
  34. #define MAXN 2000010+5 
  35. #define MAXC 26 
  36. char ch[MAXN]; 
  37. int rk[MAXN],sz[MAXN],ans[MAXN]; 
  38. int T; 
  39. struct Suffix_Automaton{ 
  40.     int child[MAXN][MAXC]; 
  41.  
  42.     int f[MAXN];//指向最近的接受态 
  43.     int num[MAXN];//匹配个数 
  44.     int l[MAXN];//此节点代表的最长串的长度 
  45.     int head,tail,tot;//头节点,最后的接受态,总节点 
  46.     void clear(){ 
  47.         memset(child,0,sizeof(child)); 
  48.         memset(f,0,sizeof(f)); 
  49.         memset(l,0,sizeof(l)); 
  50.         cle(num); 
  51.         head=tail=tot=1
  52.     } 
  53.     void add(int c){ 
  54.         int p=tail,np=++tot;l[np]=l[p]+1;tail=np; 
  55.         for(;p&&!child[p][c];p=f[p]) child[p][c]=np; 
  56.         if(!p) f[np]=head; 
  57.         else if(l[child[p][c]]==l[p]+1) f[np]=child[p][c]; 
  58.         else
  59.             int q=child[p][c],r=++tot; 
  60.             memcpy(child[r],child[q],MAXC*sizeof(int)); 
  61.             f[r]=f[q];l[r]=l[p]+1;f[q]=f[np]=r; 
  62.             for(;p&&child[p][c]==q;p=f[p]) child[p][c]=r; 
  63.         } 
  64.     } 
  65.     void insert(char*ch){ 
  66.         int l=strlen(ch); 
  67.         for(int i=0;i<l;i++) add(ch[i]-'a')
  68.    } 
  69.     int ws[MAXN],used[MAXN]; 
  70.     void tpu(){ 
  71.         int len=strlen(ch); 
  72.         int p=head; 
  73.         for(int i=0;i<len;i++){ 
  74.              p=child[p][ch[i]-'a']
  75.              num[p]=1;//自己本身的前缀串标记 
  76.         } 
  77.         for(int i=0;i<=len;i++)ws[i]=0
  78.         for(int i=1;i<=tot;i++)ws[l[i]]++; 
  79.         for(int i=1;i<=len;i++)ws[i]+=ws[i-1]; 
  80.         for(int i=tot;i>0;i--)rk[ws[l[i]]--]=i; 
  81.         for(int i=tot;i>0;i--){//沿父节点向上更新 
  82.             num[f[rk[i]]]+=num[rk[i]];//个数和 
  83.         } 
  84.             T=0;cle(used); 
  85.         } 
  86.          int solve(char *ch)//最长公共子串 
  87.         { 
  88.         int len=strlen(ch); 
  89.         int temp=0
  90.         int p=head; 
  91.         int ans=0
  92.         int i; 
  93.         ++T;//标记被那次使用了 
  94.         for(i=0;i<len;i++){ 
  95.             int ip=ch[i]-'a'; 
  96.             while(p&&!child[p][ip]){//找到匹配位置 
  97.                 p=f[p]; 
  98.             } 
  99.             if(!p){ 
  100.                 temp=0
  101.                 p=head; 
  102.             } 
  103.             else
  104.                 temp=min(temp,l[p])+1
  105.                 p=child[p][ip]; 
  106.             } 
  107.             while(l[f[p]]>=len/2){//退回到匹配长度 
  108.                 p=f[p]; 
  109.                 temp=l[p]; 
  110.             } 
  111.             if(temp>=len/2&&used[p]!=T) 
  112.             { 
  113.                 used[p]=T; 
  114.                 ans+=num[p]; 
  115.             } 
  116.         } 
  117.         return ans; 
  118.     } 
  119. }SAM; 
  120. int main() 
  121. #ifndef ONLINE_JUDGE 
  122.      freopen("in.txt","r",stdin); 
  123.      //freopen("out.txt","w",stdout); 
  124. #endif 
  125.     scanf("%s",&ch); 
  126.     SAM.clear(); 
  127.     SAM.insert(ch); 
  128.     SAM.tpu(); 
  129.     int n; 
  130.     cin>>n; 
  131.     int i,j; 
  132.     rep(j,n) 
  133.     { 
  134.         scanf("%s",&ch); 
  135.         int len=strlen(ch); 
  136.         for(i=0;i<len;i++){ 
  137.             ch[i+len]=ch[i]; 
  138.         } 
  139.         ch[i+len]='\\0'; 
  140.         printf("%d\\n",SAM.solve(ch)); 
  141.     } 
  142.     return 0
  143. }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值