链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3587
题意:给出两个字符串S和T。S,T<=100000.拿出S的两个子串(能够重叠),将两个子串连接起来成为字符串T的方法有多少种。
思路:用扩展KMP求出S的从每位開始的子串与T的公共前缀,再将两个子串翻转,再用扩展KMP求出S反的从每位開始的子串与T反的公共前缀。找出当中和为T子串长度的S公共前缀和S反的公共前缀的数量,相乘为结果。
代码:
#include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <ctype.h> #include <iostream> #include <map> #include <queue> #include <set> #include <stack> #include <string> #include <vector> #define eps 1e-8 #define INF 0x7fffffff #define maxn 10005 #define PI acos(-1.0) #define seed 31//131,1313 typedef long long LL; typedef unsigned long long ULL; using namespace std; const int N = 101010; int next_a[N],extand_a[N],next_c[N],extand_c[N]; void getnext(char *T,int *next,int *extand) // next[i]: 以第i位置開始的子串 与 T的公共前缀 { int i,length = strlen(T); next[0] = length; for(i = 0; i<length-1 && T[i]==T[i+1]; i++); next[1] = i; int a = 1; for(int k = 2; k < length; k++) { int p = a+next[a]-1, L = next[k-a]; if( (k-1)+L >= p ) { int j = (p-k+1)>0?(p-k+1) : 0; while(k+j<length && T[k+j]==T[j]) j++;// 枚举(p+1,length) 与(p-k+1,length) 区间比較 next[k] = j, a = k; } else next[k] = L; } } void getextand(char *S,char *T,int *next,int *extand) //s是母串,t是模式串 { memset(next,0,sizeof(next)); getnext(T,next,extand); int Slen = strlen(S), Tlen = strlen(T), a = 0; int MinLen = Slen>Tlen?
Tlen:Slen; while(a<MinLen && S[a]==T[a]) a++; extand[0] = a, a = 0; for(int k = 1; k < Slen; k++) { int p = a+extand[a]-1, L = next[k-a]; if( (k-1)+L >= p ) { int j = (p-k+1)>0? (p-k+1) : 0; while(k+j<Slen && j<Tlen && S[k+j]==T[j] ) j++; extand[k] = j; a = k; } else extand[k] = L; } } char a[100005],b[100005],c[100005],d[100005]; LL t_a[100005],t_c[100005]; void init() { memset(t_a,0,sizeof(t_a)); memset(t_c,0,sizeof(t_c)); } int main() { //freopen("1.txt","r",stdin); int T; scanf("%d",&T); while(T--) { init(); scanf("%s",a); scanf("%s",b); int len_a=strlen(a); for(int i=0;i<len_a;i++) c[i]=a[len_a-1-i]; c[len_a]='