题目链接:http://poj.org/status?problem_id=3376
题意给出一连串多组字符串,问这些字符串两两相连能形成多少个回文串。
参考别人的思路,看了下扩展kmp,才明白怎么解决这一题~
如果两个字符串的公共前缀为其中某一个的整,并且另一个串的余下部分为回文串,则一个字符串与另一个字符串的逆序串边接可成一回文串。
所以这题中,将字符串分原串和逆串进行处理,在字典树中找公共前缀, 而非公共前缀部分的回文串判断,用扩展kmp解决。
Code:
#include<stdio.h> #include<string.h> #include<stdlib.h> #define N 2000012 char s[N]; int len,n; int f[N],t[N]; typedef struct talT{ int c,leaf; talT *nxt[27]; }Trie; Trie Memory[N*3]; Trie *r; int size; bool bis[N]; int nxt[N],ext[N]; Trie *NewTrie() { return &Memory[size++]; } void Insert(int ix) { int i,a; Trie *cur=r; for(i=f[ix];i<t[ix];i++){ cur->c+=bis[i]; a=s[i]&31; if(!cur->nxt[a]) cur->nxt[a]=NewTrie(); cur=cur->nxt[a]; } cur->leaf++; } void ExtendKMP(char s[],char t[],int l,int ix) { int i,j,k; int Len,L; //next j=0; while(t[j]==t[1+j]&&j+1<l) j++; nxt[1]=j,k=1; for(i=2;i<l;i++){ Len=k+nxt[k],L=nxt[i-k]; if(Len>L+i) nxt[i]=L; else{ j=Len-i>0?Len-i:0; while(t[i+j]==t[j]&&i+j<l) j++; nxt[i]=j,k=i; } } //extend j=0; while(s[j]==t[j]&&j<l) j++; ext[0]=j,k=0; for(i=1;i<l;i++){ Len=k+ext[k],L=nxt[i-k]; if(Len>L+i) ext[i]=L; else{ j=Len-i>0?Len-i:0; while(s[i+j]==t[j]&&i+j<l) j++; ext[i]=j,k=i; } } for(i=0;i<l;i++){ if(ext[i]==l-i) bis[ix+i]=1; } } __int64 Query() { int i,j; __int64 ret; int a; Trie *cur; for(ret=i=0;i<n;i++){ cur=r; for(j=t[i];j<f[i+1];j++){ a=s[j]&31; cur=cur->nxt[a]; if(!cur) break; if(bis[j+1]||j+1==f[i+1]) ret+=cur->leaf; } if(cur) ret+=cur->c; } return ret; } int main() { int i,j,l; scanf("%d",&n); len=0;size=0; r=NewTrie(); for(i=0;i<n;i++){ scanf("%d%s",&l,s+len); for(j=0;j<l;j++) s[len+(l<<1)-j-1]=s[len+j]; ExtendKMP(s+len,s+len+l,l,len); ExtendKMP(s+len+l,s+len,l,len+l); f[i]=len; t[i]=len+l; len+=l<<1; Insert(i); } f[i]=len; printf("%I64d\n",Query()); return 0; }