1. 程式人生 > >poj3376 Finding Palindromes【exKMP】【Trie】

poj3376 Finding Palindromes【exKMP】【Trie】

Finding Palindromes
Time Limit: 10000MS   Memory Limit: 262144K
Total Submissions:4710   Accepted: 879
Case Time Limit: 2000MS

Description

A word is called a palindrome if we read from right to left is as same as we read from left to right. For example, "dad", "eye" and "racecar" are all palindromes, but "odd", "see" and "orange" are not palindromes.

Given n strings, you can generate n × n pairs of them and concatenate the pairs into single words. The task is to count how many of the so generated words are palindromes.

Input

The first line of input file contains the number of strings n. The following n

 lines describe each string:

The i+1-th line contains the length of the i-th string li, then a single space and a string of li small letters of English alphabet.

You can assume that the total length of all strings will not exceed 2,000,000. Two strings in different line may be the same.

Output

Print out only one integer, the number of palindromes.

Sample Input

3
1 a
2 ab
2 ba

Sample Output

5

Hint

The 5 palindromes are: 
a a a ba ab a ab ba ba ab 

Source

POJ Monthly--2007.09.09, Zhou Gelin, modified from POI06   題意: 給定n個字串。問把他們兩兩組合起來,能有多少個迴文串。 思路: 兩個字串$s,t$進行組合有三種情況。 1、$lens=lent$,若$s$和$t$對稱,那麼可以$st$是一個迴文串。 2、$lens>lent$,若$t$和$s$的字首對稱,$s$剩餘的部分是一個迴文,那麼$st$是一個迴文串。 3、$lens<lent$,若$s$和$t$的字首對稱,$t$剩餘的部分是一個迴文,那麼$ts$是一個迴文串。 現在我們對所有字串建立一個字典樹,然後用每一個反向串在樹上進行匹配。 對於第一種可能,很簡單,反向串匹配完的那個節點,是$cnt$個字串的結尾,$ans+=cnt$ 對於第二種可能,反向串不能繼續匹配了之後,如果剩餘的串是是迴文,那麼也是$ans += cnt$ 對於第三種可能,反向串匹配到結尾之後,如果樹上這個節點之後有$val$個迴文,那麼$ans+=val$ 因此我們現在需要在Trie樹上維護兩個值,$cnt$和$val$   exKMP我們可以求出某個點之後是不是迴文串。 將$s$作為模式串,與反向串$s'$進行exkmp。若$ex[i] = len - i + 1$,那麼$s'[i...len]$是一個迴文串。 同理將$s'$作為模式串,與$s$進行exkmp。 由此可以在建立Trie樹時確定$cnt$和$val$   由於題目沒有說$n$的範圍,只給定了字串的總長。所以我們只能把所有字串都存在同一個字串中,用陣列記錄開始和結束下標。  
  1 #include<iostream>
  2 //#include<bits/stdc++.h>
  3 #include<cstdio>
  4 #include<cmath>
  5 //#include<cstdlib>
  6 #include<cstring>
  7 #include<algorithm>
  8 //#include<queue>
  9 #include<vector>
 10 //#include<set>
 11 //#include<climits>
 12 //#include<map>
 13 using namespace std;
 14 typedef long long LL;
 15 typedef unsigned long long ull;
 16 #define N 100010
 17 #define pi 3.1415926535
 18 #define inf 0x3f3f3f3f
 19 
 20 const int maxn = 2e6 + 6;
 21 int n, sum_len;
 22 char s[maxn], s_rev[maxn];
 23 int st[maxn], ed[maxn];
 24 
 25 int nxt[maxn], ex[maxn];
 26 bool flag[2][maxn];
 27 void GETNEXT(char *str, int l, int r)
 28 {
 29     int i=l,j,po;
 30     nxt[i]=r - l + 1;//初始化next[0]
 31     while(str[i]==str[i+1]&&i<=r)//計算next[1]
 32     i++;
 33     nxt[l + 1]=i - l;
 34     po=1 + l;//初始化po的位置
 35     for(i=2+l;i<=r;i++)
 36     {
 37         if(nxt[i-po + l]+i - l<nxt[po]+po - l)//第一種情況,可以直接得到next[i]的值
 38         nxt[i]=nxt[i-po + l];
 39         else//第二種情況,要繼續匹配才能得到next[i]的值
 40         {
 41             j=nxt[po]+po-i;
 42             if(j<0)j=0;//如果i>po+next[po],則要從頭開始匹配
 43             while(i+j<=r&&str[l+j]==str[j+i])//計算next[i]
 44             j++;
 45             nxt[i]=j;
 46             po=i;//更新po的位置
 47         }
 48     }
 49 }
 50 //計算extend陣列
 51 void EXKMP(char *s1,char *s2, int l, int r, int sign)
 52 {
 53     int i=l,j,po;
 54     GETNEXT(s2, l, r);//計運算元串的next陣列
 55     //for(j = l; j <= r; j++)cout<<nxt[j]<<endl;
 56     while(s1[i]==s2[i]&&i<=r)//計算ex[0]
 57     i++;
 58     ex[l]=i - l;
 59     po=l;//初始化po的位置
 60     for(i=l + 1;i<=r;i++)
 61     {
 62         if(nxt[i-po + l]+i - l<ex[po]+po -l)//第一種情況,直接可以得到ex[i]的值
 63         ex[i]=nxt[i-po+l];
 64         else//第二種情況,要繼續匹配才能得到ex[i]的值
 65         {
 66             j=ex[po]+po-i;
 67             if(j<0)j=0;//如果i>ex[po]+po則要從頭開始匹配
 68             while(i+j<=r&&s1[j+i]==s2[l+j])//計算ex[i]
 69             j++;
 70             ex[i]=j;
 71             po=i;//更新po的位置
 72         }
 73     }
 74     for(int i = l; i <= r; i++){
 75         //cout<<ex[i]<<endl;
 76         if(ex[i] == r - i + 1){
 77             flag[sign][i] = true;
 78         }
 79     }
 80 }
 81 
 82 struct Trie{
 83     int trie[maxn][26];
 84     int cnt[maxn];
 85     int val[maxn];
 86     int tot;
 87 }tr;
 88 
 89 void init()
 90 {
 91     memset(flag, 0, sizeof(flag));
 92     memset(nxt, 0, sizeof(nxt));
 93     memset(ex, 0, sizeof(ex));
 94     for(int i = 0; i <= tr.tot; i++){
 95         memset(tr.trie[i], 0, sizeof(tr.trie[i]));
 96         tr.cnt[i] = 0;
 97         tr.val[i] = 0;
 98     }
 99     tr.tot = 0;
100 }
101 
102 void insertt(char *str, int l, int r)
103 {
104     int p = 0;
105     for(int k = l; k <= r; k++){
106         int ch = str[k] - 'a';
107         tr.val[p] += flag[0][k];
108         if(tr.trie[p][ch] == 0){
109             tr.trie[p][ch] = ++tr.tot;
110         }
111         p = tr.trie[p][ch];
112 
113     }
114     tr.cnt[p]++;
115 }
116 
117 LL searchh(char *str, int l, int r)
118 {
119     LL ans = 0;
120     int p = 0;
121     for(int k = l; k <= r; k++){
122         //cout<<flag[k]<<endl;
123         int ch = str[k] - 'a';
124         p = tr.trie[p][ch];
125         if(p == 0)break;
126         if(k < r && flag[1][k + 1] || k == r)ans += tr.cnt[p];
127     }
128     if(p)ans += tr.val[p];
129     //ans += tr.cnt[p];
130     return ans;
131 }
132 
133 int main()
134 {
135     while(scanf("%d", &n) != EOF){
136         sum_len = 0;
137         init();
138         for(int i = 0; i < n; i++){
139             int l;
140             scanf("%d %s", &l, s + sum_len);
141             for(int j = 0; j < l; j++){
142                 s_rev[sum_len + j] = s[sum_len + l - 1 - j];
143             }
144             st[i] = sum_len;
145             sum_len += l;
146             ed[i] = sum_len - 1;
147 
148             EXKMP(s, s_rev, st[i], ed[i], 0);
149             EXKMP(s_rev, s, st[i], ed[i], 1);
150             insertt(s, st[i], ed[i]);
151         }
152 
153         LL ans = 0;
154         /*for(int i = 0; i < sum_len; i++){
155             cout<<s[i]<<" "<<flag[0][i]<<" "<<flag[1][i]<<endl;
156         }*/
157         //cout<<s<<endl;
158         for(int i = 0; i < n; i++){
159             ans += searchh(s_rev, st[i], ed[i]);
160         }
161         printf("%lld\n", ans);
162     }
163     return 0;
164 }