测试地址:Palindrome
题目大意:求一个把一个字符串分割成两部分的方案,使得前面的部分包含的本质不同的回文子串数是后面的部分包含的本质不同的回文子串数的两倍,输出前面的部分所包含的字符数,如果有多个方案,输出全部方案中前面部分字符数的乘积,对10^9+7取模,如果没有方案输出0。
做法:看到“本质不同的回文子串”本能的想到回文自动机。求一个字符串包含的本质不同回文子串数的方法,就是对该字符串构建好回文自动机后,答案就是自动机的节点数-2(因为有两个空节点)。然而这题要把字符串分割成两部分并分别求两部分所包含的本质不同回文子串数,实际上我们可以用lft[i]表示字符串前i个字符构成的前缀中包含的本质不同回文子串数,rht[i]表示字符串后len-i+1个字符构成的后缀中包含的本质不同回文子串数。求前缀中包含的本质不同回文子串数,我们只需要每次插入节点后记录答案即可,求后缀中包含的本质不同回文子串数方法类似,但是插入要从后往前插入,因为回文串翻转之后还是回文串,所以没有影响。最后答案就是所有满足lft[i]=2*rht[i](1≤i≤n-1)的i的乘积。
自己犯二的地方:总是忘记新节点的fail指针要指的是getfail(fail[now])的c儿子...偏偏还过了样例数据,查了半小时...
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define mod 1000000007
using namespace std;
char s[100010];
int T,n,tot=0,fail[100010],len[100010],ch[100010][30],pos,now;
int lft[100010],rht[100010];
ll ans;
bool flag;
int getfail(int x)
{
while(s[pos-len[x]-1]!=s[pos]) x=fail[x];
return x;
}
void insert(int c)
{
now=getfail(now);
if (!ch[now][c])
{
int son=++tot;
ch[now][c]=son;
fail[son]=ch[getfail(fail[now])][c];
len[son]=len[now]+2;
for(int i=0;i<=25;i++) ch[son][i]=0;
}
now=ch[now][c];
}
int main()
{
scanf("%d",&T);
while(T--)
{
s[0]='#';
scanf("%s",&s[1]);
n=strlen(s)-1;
for(int i=0;i<=25;i++) ch[0][i]=ch[1][i]=ch[n+1][i]=0;
fail[0]=1,fail[1]=n+1;
len[0]=0,len[1]=len[n+1]=-1;
tot=1;now=0;
for(pos=1;pos<=n;pos++)
{
insert(s[pos]-'a');
lft[pos]=tot-1;
}
for(int i=1;i<=n/2;i++)
swap(s[i],s[n-i+1]);
for(int i=0;i<=25;i++) ch[0][i]=ch[1][i]=ch[n+1][i]=0;
fail[0]=1,fail[1]=n+1;
len[0]=0,len[1]=len[n+1]=-1;
tot=1;now=0;
for(pos=1;pos<=n;pos++)
{
insert(s[pos]-'a');
rht[n-pos+1]=tot-1;
}
ans=1;flag=0;
for(ll i=1;i<n;i++)
if (lft[i]==2*rht[i+1]) {ans=(ans*i)%mod;flag=1;}
if (flag) printf("%lld
",ans);
else printf("0
");
}
return 0;
}