题意:把一个串分成2部分,求左半部份的本质不同回文串个数等于右半部份本质不同回文串个数的2倍的所有位置乘起来的答案。
做法:学习了下回文树,总结几点。
1.用编号去代表每个不同的回文串。比如编号为5的回文串为aba,那么next[5]['z']就是zabaz。next[i][j]保存的是这个在编号为i回文串的两端加字母j的回文串的编号。
2.fail[i]代表编号为i的回文串的包含在内具有相同右端点的最长回文串。。例如ababa,下一个是aba,再下一个是a。
3.类似ac自动机的失配做法。通过fail尝试所有以前一个字母为右端点的回文串,例如长度是L,那么就判断当前位置pos-1-L是否与pos字母相同。相同就匹配成功。不相同就继续fail,匹配成功即是以当前位置为右端点的最长的回文串。
4.在求出最长回文串后得看看是否是新的,那么这个就要利用到next,例如当前字母为a,匹配成功的前一个回文串编号为last,那么就要看next[last]['a']是否不为0,为0说明当前串是新串(那么就要加入),反之就是已经出现过。
5.如果是新串就需要求fail指针,是为了后面一个加入字母的匹配。我们继续类似3的方法,继续fail[last],求是否匹配。即求下一个以当前位置为右端点的回文串。注意这里找到的回文串必定已经出现过了。因为当前的串为回文串,那么找到的通过对称可以发觉左边已经出现过了。所以可以用fail[i] = next[last][a]。
6.需要设定边界。即偶数边界和奇数边界,偶数边界的len为0,奇数边界的len为-1,这都是因为pos-1-L所决定的。偶数边界就是与前一个比较,奇数边界就是与自己比较(必定能匹配啦)。
网上对比了一些代码。删去了一些无用的语句。感觉比较简洁易懂了。
AC代码:
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<string.h>
#include<string>
#include<sstream>
#include<bitset>
using namespace std;
#define ll long long
#define ull unsigned long long
#define eps 1e-8
#define MAXN 100000+100
#define MOD 1000000007
#define palin(x, id, c) (s[id - 1 - l[x]] - 'a' == c)
char str[MAXN],str2[MAXN];
struct PalindromeTree
{
int sz, odd_, even, last;
int next[MAXN][26], fail[MAXN], l[MAXN];
void Init(char *s)
{
sz = 0;
odd_ = ++sz, l[odd_] = -1, fail[odd_] = odd_;
memset(next[sz],0,sizeof(next[sz]));
even = ++sz, l[even] = 0, fail[even] = odd_;
memset(next[sz],0,sizeof(next[sz]));
last = odd_;
s[0] = '$';
}
void Add(int c, int id, char *s)
{
while(!palin(last, id, c)) last = fail[last];
if (next[last][c]) last = next[last][c];
else
{
int x = last;
++sz;
memset(next[sz],0,sizeof(next[sz]));
next[x][c] = sz, l[sz] = l[x] + 2;
if (x == odd_) fail[sz] = even;
else
{
x = fail[x];
while(!palin(x, id, c)) x = fail[x];
fail[sz] = next[x][c];
}
last = sz;
}
}
}pt;
int ans1[MAXN],ans2[MAXN];
int main()
{
#ifdef GLQ
freopen("input.txt","r",stdin);
// freopen("o.txt","w",stdout);
#endif
int cas = 1,t;
scanf("%d\n",&t);
while(t--)
{
gets(str+1);
pt.Init(str);
int len = strlen(str+1);
for(int i = 1; i <= len; i++)
{
pt.Add(str[i] - 'a', i, str);
ans1[i] = pt.sz-2;
str2[len-i+1] = str[i];
}
pt.Init(str2);
for(int i = 1; i <= len; i++)
{
pt.Add(str2[i] - 'a', i, str2);
ans2[len-i+1] = pt.sz-2;
}
ll ret = 1;
for(int i = 1; i <= len-1; i++)
{
// cout<<i<<" "<<ans1[i]<<" "<<ans2[i+1]<<endl;
if(ans1[i] == ans2[i+1]*2) ret = ret*(ll)i%MOD;
}
if(ret == 1) ret = 0;
printf("%lld\n",ret);
}
return 0;
}