POJ 3376 Finding Palindromes(manacher求前后缀回文串+trie)

题目链接: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];//End[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 }

 

转载于:https://www.cnblogs.com/fu3638/p/8503514.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值