無名(noname) 字符串
【问题描述】
因为是蒯的题所以没想好名字,为什么要用繁体呢?去看《唐诗三百首》吧!
题意很简单,给你一个串,求他有多少个不同的子串,满足前缀为A,后缀为B。
需要注意的是,串中所有的字母都是小写字母。
友情提示:如果你过不了样例,请注意是不同的子串。
【输入文件】
一共3行。
第一行母串S;
第二行串A;
第三行串B。
【输出文件】
一个数,即有多少不同的子串。
【输入样例】
abababab a b |
【输出样例】
4 |
【数据规模和约定】
100%:
length(S)<=2000;
length(A)<=2000;
length(B)<=2000;
30%:都少个0。
首先我们可以枚举子串是吧,这样首先就来了一个n^2的复杂度,然后我们判断前缀和后缀,这些其实没什么太难的部分,我们只要先预处理是否是前缀和后缀,就可以很方便的解决了。
然后问题来了,我们怎么在字符串上判重呢?
这里我们就要引入字符串hash了。
我们把S字符串从0到i的字符串hash止记录在hash【i】中,然后我们就可以得到前缀的字符串hash了。
hash[i]=(hash[i-1]*p+S[i]-'a'+1)%t;
那么其它位怎么表示呢?
我们可以令字符串S【i】~S[j]=(hash[j]-hash[i-1]*p^(j-i+1))%t;(这里p的次方可以首先求好存好)。
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define ll long long #define il inline #define db double using namespace std; ll p=31,t=100000007; ll hash[2045]; bool vis[100000045]; ll ci[2045]; il ll find(int x,int y) { return (hash[y]+t-hash[x-1]*ci[y-x+1]%t)%t; } bool qian[2045]; bool hou[2045]; char S[2045]; char A[2045]; char B[2045]; int len,len1,len2; int main() { freopen("noname.in","r",stdin); freopen("noname.out","w",stdout); ci[0]=1; for(int i=1;i<=2000;i++) ci[i]=(ci[i-1]*p)%t; scanf("%s%s%s",S,A,B); len=strlen(S); len1=strlen(A); len2=strlen(B); // printf("p=%lld,t=%lld\n",p,t); hash[0]=S[0]-'a'+1; for(int i=1;i<len;i++) hash[i]=(hash[i-1]*p+S[i]-'a'+1)%t; // for(int i=1;i<len;i++) // printf("hash[i]=%lld\n",hash[i]); for(int i=0;i<len-len1+1;i++) { int l=0; for(int j=0;j<len1;j++) if(S[i+j]==A[j]) l++; if(l==len1) qian[i]=1; } for(int i=len-1;i>=len2-1;i--) { int l=0; for(int j=0;j<len2;j++) if(S[i-len2+j+1]==B[j]) l++; if(l==len2) hou[i]=1; } // for(int i=0;i<len;i++) // printf("%d %d\n",qian[i],hou[i]); int ans=0; for(int i=0;i<len;i++) for(int j=i;j<len;j++) { // printf("i=%d j=%d\n",i,j); if(qian[i]&&hou[j]&&j-i>=len1-1&&j-i>=len2-1) { ll now=find(i,j); // printf("now=%lld\n",now); if(!vis[now]) { ans++; vis[now]=1; } } } printf("%d\n",ans); return 0; }
PEACE