题目链接
题目大意
有一个串s 和一个串 t,现在让你从串s中截一个子串k和t中的一个前缀p,k和p连起来要是一个回文串,k要比p长
解题思路
我们设从S截的子串为S【i–j】T的前缀为T【1–k】,那么S【i—i+k】和T【1—k】一定是反转的,也就是把S反转一下和T是相同的,剩下的S【i+k+1–j】是个回文串
所以我们把S反转一下,和T做扩展KMP,得到ex数组再反转一下,就是S【i】为止能和T形成会回文的个数,中间的部分,用回文自动机,得到中间串的长度,两个数相乘就是答案
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=1e6+6;
char s[N],s1[N],t[N];
int nex[N],ex[N],b[N];
struct PAM
{
int len[N];
int nex[N][30];
int fail[N];
int cnt[N];
int num[N];
int s[N];
int last,p,n;
int newnode(int l)
{
for(int i=0;i<=26;i++)
nex[p][i]=0;
cnt[p]=0;
num[p]=0;
len[p]=l;
return p++;
}
void init()
{
p=0;
newnode(0);
newnode(-1);
last = 0;
n = 0;
s[n] = -1;
fail[0] = 1;
}
int getfail(int x)
{
while(s[n-len[x]-1]!=s[n])
x=fail[x];
return x;
}
int add(int c)
{
c-='a';
s[++n]=c;
int cur=getfail(last);
if(!nex[cur][c])
{
int now=newnode(len[cur]+2);
fail[now]=nex[getfail(fail[cur])][c];
nex[cur][c]=now;
num[now]=num[fail[now]]+1;
}
last=nex[cur][c];
cnt[last]++;
return num[last];
}
void countt()
{
for(int i=p-1;i>=0;i--)
cnt[fail[i]]+=cnt[i];
}
}Tree;
void getNext(char str[])
{
int i=0,j,po,len=strlen(str);
nex[0]=len;
while(str[i]==str[i+1] && i+1<len) i++; nex[1]=i;
po=1;
for(i=2;i<len;i++)
{
if(nex[i-po]+i < nex[po]+po)
nex[i]=nex[i-po];
else
{
j = nex[po]+po-i;
if(j<0) j=0;
while(i+j<len && str[j]==str[j+i]) j++; nex[i]=j;
po=i;
}
}
}
void EXKMP(char s1[],char s2[])
{
int i=0,j,po,len=strlen(s1),l2=strlen(s2);
getNext(s2);
while(s1[i]==s2[i] && i<l2 && i<len) i++; ex[0]=i;
po=0;
for(i=1;i<len;i++)
{
if(nex[i-po]+i < ex[po]+po)
ex[i]=nex[i-po];
else
{
j = ex[po]+po-i;
if(j<0) j=0;
while(i+j<len && j<l2 && s1[j+i]==s2[j]) j++; ex[i]=j;
po=i;
}
}
}
int main()
{
scanf("%s",s);
scanf("%s",t);
int len1=strlen(s);
int len2=strlen(t);
for(int i=0;i<len1;i++)
s1[i]=s[len1-i-1];
EXKMP(s1,t);
reverse(ex,ex+len1);
Tree.init();
for(int i=len1-1;i>=0;i--)
b[i]=Tree.add(s[i]);
Tree.countt();
long long ans=0;
for(int i=0;i<len1;i++)
ans+=(long long)b[i+1]*(long long)ex[i];
printf("%lld\n",ans);
return 0;
}