1. 程式人生 > >POJ 3376 Finding Palindromes(manacher求前後綴回文串+trie)

POJ 3376 Finding Palindromes(manacher求前後綴回文串+trie)

!= pri min ret 長度 ems algo 邊緣 字符串

題目鏈接:http://poj.org/problem?id=3376

題目大意:給你n個字符串,這n個字符串可以兩兩組合形成n*n個字符串,求這些字符串中有幾個是回文串。

解題思路:思路參考了這裏:http://blog.csdn.net/qq_30241305/article/details/50718051

做法:首先由兩個字符串A,B.要使它們能組成回文串有三種情況

情況① :alen < blen

    abc  abacba  abcaba   

     A B    RB

如上所示,A是B的反串前綴,且b的剩余部分可以認為是後綴是回文串

情況②:alen > blen

  abcaba  cba  abc

   A    B   RB

B的反串是A的前綴,且a的後綴是回文串

情況③:alen == blen

b是a的反串,這個就不用解釋了吧。

現將所有正序字符(原字符串)串建立在字典樹中。然後用反串去匹配,根據上面給出的三種情況作出判斷。

另外:因為只給了字符串的總長度,所以,只開一維的字符串數組,每次接著上次字符串結束的位置開始即可。為了方便,記錄字符串開始的位置,和結束的位置。

代碼:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4
#include<algorithm> 5 using namespace std; 6 typedef long long LL; 7 const int N=2e6+5; 8 9 LL ans; 10 int n,len1,len2,idx,root; 11 char s[N],str[N*2]; 12 int trie[N][27],p[N*2],End[N],hou[N];//cnt[i]表示以節點i為結尾的字符串的數目 13 //hou[i]表示節點i之後(不包括i)的後綴回文串數目
14 15 struct node{ 16 int st,len; 17 }a[N]; 18 19 void init(){ 20 str[0]=$; 21 str[1]=#; 22 len1--; 23 for(int i=1;i<=len1;i++){ 24 str[i*2]=s[i]; 25 str[i*2+1]=#; 26 } 27 len2=len1*2+2; 28 str[len2]=@; 29 } 30 31 void manacher(){ 32 init(); 33 int id=-1,mx=-1; 34 for(int i=1;i<len2;i++){ 35 if(mx>i) p[i]=min(p[id*2-i],mx-i); 36 else p[i]=1; 37 while(str[i-p[i]]==str[i+p[i]]) 38 p[i]++; 39 if(i+p[i]>mx){ 40 mx=i+p[i]; 41 id=i; 42 } 43 } 44 } 45 46 //將字符串正序插入字典樹中 47 void Insert(){ 48 for(int i=0;i<n;i++){ 49 int now=root; 50 for(int j=a[i].st;j<a[i].st+a[i].len;j++){ 51 if(!trie[now][s[j]-a]) trie[now][s[j]-a]=++idx; 52 now=trie[now][s[j]-a]; 53 int mid=((j+1)*2-1+(a[i].st+a[i].len-1)*2+1)/2;//(j+1)*2是s[j+1]對應str的位置(j+1)*2-1則是邊緣的‘#‘,同理(a[i].st+a[i].len-1)*2+1也對應邊緣‘#‘ 54 if(p[mid]>mid-(j+1)*2+1) 55 hou[now]++; 56 } 57 hou[now]--; //因為字符串末尾的"#"也算成一個回文串了,所以要減掉 58 End[now]++; 59 } 60 } 61 62 //計算可以生成的回文串數 63 void Find(){ 64 for(int i=0;i<n;i++){ 65 int now=root; 66 for(int j=a[i].st+a[i].len-1;j>=a[i].st;j--){ 67 //匹配失敗,沒有對應的字符串 68 if(!trie[now][s[j]-a]){ 69 now=-1; 70 break; 71 } 72 now=trie[now][s[j]-a]; 73 if(End[now]){ //情況①或③ 74 int mid=(a[i].st*2-1+(j-1)*2+1)/2; 75 if(p[mid]>mid-a[i].st*2+1) 76 ans+=End[now]; 77 } 78 } 79 if(now!=-1) ans+=hou[now]; //情況② 80 } 81 } 82 83 int main(){ 84 while(~scanf("%d",&n)){ 85 idx=0,len1=1,ans=0; 86 memset(trie,0,sizeof(trie)); 87 memset(End,0,sizeof(End)); 88 memset(hou,0,sizeof(hou)); 89 for(int i=0;i<n;i++){ 90 scanf("%d",&a[i].len); 91 scanf("%s",s+len1); 92 a[i].st=len1; 93 len1+=a[i].len; 94 } 95 manacher(); 96 Insert(); 97 Find(); 98 printf("%lld\n",ans); 99 } 100 return 0; 101 }

POJ 3376 Finding Palindromes(manacher求前後綴回文串+trie)