B - Finding Palindromes (字典树+manacher)

题目链接:https://cn.vjudge.net/contest/283743#problem/B

题目大意:给你n个字符串,然后问你将这位n个字符串任意两两组合,然后问你这所有的n*n种情况中,是回文串的有多少个?

题目大意:学到了一个很骚气的存储多个零散字符串的方法,因为有可能个给你很多零散的字符串,我们可以将这些字符串存储在一个字符串里面,然后再额外加一个数组记录每一个字符串的开始位置和截止位置就好了。 然后是对于这个题,首先说一下判断字符串的方法,对于每一个字符串我们通过manacher算法求出每一个点的最长的回文串,然后在字典树中反序存储这个字符串。

举个例子:aaba和baa,这个时候baa插在aaba后面是可以构成回文串的,在字典树中baa是倒序存储的,所以就能够和aaba的前缀aab相匹配,然后我们在看aab后面的子串是不是回文串,如果是回文串的话,这个两个就能够形成回文串,因为前面部分和后面部分是对称的,并且中间部分也是回文串。

我们对于每一个字符串,

surf[i]记录的是对于当前的字符串,从第i+1个位置开始,能不能构成字符串。

pre[i]数组记录的是对于当前的字符串,从当前字符串的第一个位置开始到第i-1个位置,前缀能不能构成回文串。

val[i]数组记录的是字典树中从第0个位置到i所形成的的字符串的个数。

qian[i]数组记录的是当前节点后面是回文串的数目。

查询的时候,一共有两种情况:

一种是题目中说的aaba和bba这种情况,到了某一个字符串的位置之后,这个位置之前的有对称的字符串,这个位置之后的是一个回文串。

另一种是一个字符串是回文串,一直到底部都是能构成回文串的,这个时候还应该加上一直到这个字符串底部的回文串的个数就可以了。

每一次查询当前节点后面是不是回文串,如果是的话就加上这个字符串的个数。如果找到了底部还需要注意后面形成的回文串的个数。

AC代码:

  1 #include<iostream>
  2 #include<stack>
  3 #include<iomanip>
  4 #include<cmath>
  5 #include<stdio.h>
  6 #include<algorithm>
  7 #include<string>
  8 #include<cstring>
  9 using namespace std;
 10 # define ll long long
 11 const int maxn = 2e6+7;
 12 const int maxm= 2e6+100;
 13 char str[maxn],tmp[maxn<<1];
 14 bool pre[maxn],suf[maxn];
 15 int pp[maxn<<1],tot;
 16 int qian[maxn],val[maxn];
 17 int start[maxn];
 18 int ch[maxn][28];
 19 void init()
 20 {
 21     for(int i=0; i<=tot; i++)
 22     {
 23         qian[i]=0,val[i]=0;
 24         for(int j=0; j<28; j++)
 25         {
 26             ch[i][j]=0;
 27         }
 28     }
 29     tot=0;
 30 }
 31 void trie(int t)
 32 {
 33     int p=0;
 34     for(int i=start[t+1]-1; i>=start[t]; i--)
 35     {
 36         int tmp=str[i]-'a';
 37         qian[p]+=pre[i];
 38         if(!ch[p][tmp])
 39             ch[p][tmp]=++tot;
 40             p=ch[p][tmp];
 41     }
 42     val[p]+=1;
 43 }
 44 void manacher(int n)
 45 {
 46     int i,mx=0,len=1,id=0;
 47     tmp[0]='$';
 48     for(i=start[n]; i<start[n+1]; i++)
 49     {
 50         tmp[len++]='#';
 51         tmp[len++]=str[i];
 52         pre[i]=0;
 53         suf[i]=0;
 54     }
 55     tmp[len]='#';
 56     tmp[len+1]=0;
 57     for(int i=2; i<len; i++)
 58     {
 59         pp[i] = 1;
 60         if(pp[id]+id > i)
 61             pp[i] = min(pp[id*2-i], pp[id]+id-i);
 62         while(tmp[ i+pp[i] ] == tmp[ i-pp[i] ])
 63             pp[i]++;
 64         if(pp[id]+id < pp[i]+i)
 65             id = i;
 66         if(pp[i]==i)
 67             pre[start[n]+pp[i]-2]=1;
 68         if(i+pp[i]-1==len)
 69             suf[start[n+1]-pp[i]+1]=1;
 70     }
 71 }
 72 ll query(int t)
 73 {
 74     ll sum=0;
 75     int p=0,i;
 76     for( i=start[t]; i<start[t+1]; i++)
 77     {
 78         int tmp=str[i]-'a';
 79         if(ch[p][tmp]==0)
 80             break;
 81             p=ch[p][tmp];
 82         if(suf[i+1]==1||i+1==start[t+1])
 83             sum+=val[p];
 84     }
 85     if(i==start[t+1])
 86         sum+=qian[p];
 87     return sum;
 88 }
 89 int main()
 90 {
 91     int n;
 92     while(~scanf("%d",&n))
 93     {
 94         init();
 95         int len;
 96         for(int i=1; i<=n; i++)
 97         {
 98             scanf("%d%s",&len,str+start[i]);
 99             start[i+1]=start[i]+len;
100             manacher(i);
101             trie(i);
102         }
103         ll ans=0;
104         for(int i=1; i<=n; i++)
105         {
106             ans+=query(i);
107         }
108         printf("%lld\n",ans);
109     }
110     return 0;
111 }

 

posted @ 2019-02-16 21:25  Let_Life_Stop  阅读(228)  评论(0编辑  收藏  举报