POJ - 3376 Finding Palindromes(拓展kmp+trie)
传送门:POJ - 3376
题意:给你n个字符串,两两结合,问有多少个是回文的;
题解:这个题真的恶心,我直接经历了5种错误类型 : ) ... 因为卡内存,所以又把字典树改成了指针版本的。
字符串s与字符串t组合是回文串的情况
1. len(s) > len(t), t的反串是 s 的前缀,且s剩下的部分是回文串 (比如s: abbcb t: ba
2. len(s) = len(t), s = t 的反串(比如s: abc t: cba
3. len(s) < len(t), s 是 t 的反串的前缀,且 t 的反串剩下的部分是回文串(比如 s: ba t: bbcb
用拓展kmp求出每个字符串的最长回文前缀和后缀(分别用原串和反串进行exkmp,用反串和原串进行exkmp就可以求出了)然后用原串建trie,用反串去匹配。
1. 在trie中, 若串在非结点位置匹配完成, 则把该节点往下走有多少个回文串累加到答案。
2. 在trie中, 若在匹配串时遇上在这个结点结束的字符串, 那么看剩下的后缀是否是回文, 若是, 则把在该点结束的字符串数目累加到答案。
将所有的字符串都放在一个数组里,用一个数组记录每个串的起点,这样能节省空间。
1 #include<iostream> 2 #include<algorithm> 3 #include<string.h> 4 #define ll long long 5 using namespace std; 6 7 ///next[i]: T[i]到T[m - 1]与T(模式串)的最长相同前缀长度; 8 ///extend[i]: S[i]到S[n - 1](原串)与T的最长相同前缀长度。 9 10 const int maxn=2000100; 11 int nt[maxn],ex[maxn],k=1,be[maxn],len[maxn]; 12 bool tmp[maxn][2]; 13 char a[maxn],b[maxn]; 14 15 struct Node 16 { 17 int val; 18 int color; 19 int tree[26]; 20 }; 21 Node z[maxn]; 22 int tot, root; 23 24 int newnode() 25 { 26 z[tot].val = 0; 27 z[tot].color = 0; 28 memset(z[tot].tree, -1, sizeof(z[tot].tree)); 29 tot++; 30 return tot-1; 31 } 32 33 //预处理计算next数组 34 void GETNEXT(char *str,int len) 35 { 36 int i=0,j,po; 37 nt[0]=len; //初始化nt[0] 38 while(str[i]==str[i+1]&&i+1<len) i++; //计算nt[1] 39 nt[1]=i; 40 po=1; //初始化po的位置 41 for(i=2;i<len;i++){ 42 if(nt[i-po]+i<nt[po]+po) nt[i]=nt[i-po]; //第一种情况,可以直接得到nt[i]的值 43 else{ //第二种情况,要继续匹配才能得到nt[i]的值 44 j=nt[po]+po-i; 45 if(j<0) j=0; //如果i>po+nt[po],则要从头开始匹配 46 while(i+j<len&&str[j]==str[j+i]) j++; //计算nt[i] 47 nt[i]=j; 48 po=i; //更新po的位置 49 } 50 } 51 } 52 53 //计算extend数组 54 void EXKMP(char *s1,int len,char *s2,int l2,int s,int flag) ///s1的后缀和s2的前缀匹配 55 { 56 int i=0,j,po; 57 GETNEXT(s2,len); //计算子串的next数组 58 while(s1[i]==s2[i]&&i<l2&&i<len) i++; //计算ex[0] 59 ex[0]=i; 60 po=0; //初始化po的位置 61 for(i=1;i<len;i++){ 62 if(nt[i-po]+i<ex[po]+po) ex[i]=nt[i-po]; //第一种情况,直接可以得到ex[i]的值 63 else{ //第二种情况,要继续匹配才能得到ex[i]的值 64 j=ex[po]+po-i; 65 if(j<0) j=0; //如果i>ex[po]+po则要从头开始匹配 66 while(i+j<len&&j<l2&&s1[j+i]==s2[j]) j++; //计算ex[i] 67 ex[i]=j; 68 po=i; //更新po的位置 69 } 70 } 71 for(int i=0;i<l2;i++) 72 if(ex[i]+i==l2) tmp[i+s][flag]=1; 73 } 74 75 void insert(char *a,int len,int s) 76 { 77 int p=root; 78 for(int i=0;i<len;i++){ 79 int c=a[i]-'a'; 80 z[p].val+=tmp[i+s][0]; 81 if(z[p].tree[c]==-1) { 82 z[p].tree[c]=newnode(); 83 } 84 p=z[p].tree[c]; 85 } 86 z[p].color++; 87 } 88 89 int query(char *a,int len,int s) 90 { 91 int p=root; 92 ll ans=0; 93 for(int i=0;i<len;i++){ 94 int c=a[i]-'a'; 95 p=z[p].tree[c]; 96 if(p==-1) break; 97 if((i<len-1&&tmp[s+i+1][1])||i==len-1) ans+=z[p].color; 98 } 99 if(p!=-1) ans+=z[p].val; 100 return ans; 101 } 102 103 int main() 104 { 105 ios::sync_with_stdio(false); 106 cin.tie(0); 107 cout.tie(0); 108 int t; 109 cin>>t; 110 int l=0; 111 tot=0; 112 root=newnode(); 113 for(int i=0;i<t;i++){ 114 cin>>len[i]>>a+l; 115 be[i]=l; 116 l+=len[i]; 117 for(int j=0;j<len[i];j++){ 118 b[j+be[i]]=a[l-1-j]; 119 } 120 EXKMP(a+be[i],len[i],b+be[i],len[i],be[i],0); 121 EXKMP(b+be[i],len[i],a+be[i],len[i],be[i],1); 122 insert(a+be[i],len[i],be[i]); 123 } 124 ll ans=0; 125 for(int i=0;i<t;i++){ 126 ans+=query(b+be[i],len[i],be[i]); 127 } 128 cout<<ans<<endl; 129 return 0; 130 }