题意:
给两个字符串S、T
问有多少对子串A、B
满足A属于S串、B属于T串
且A==B同时A是回文串
分析:
对两个串建两个回文树,然后dfs求相同的回文串
当匹配到的时候
假设A串出现x次,B串出现y次
因为可以互相匹配所以答案累加xy
code:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define ll long long using namespace std; const int maxm=3e5+5; struct Pam{ int nt[maxm][26];//下一个节点 int fail[maxm];//失配指针 int cnt[maxm];//节点i表示的本质不同的串的个数(最后cal完才是准确的) int num[maxm];//节点i表示的回文串的回文后缀个数 int len[maxm];//节点i表示的回文串长度 int s[maxm];//存放添加的字符 int last;//新添加一个字母后所形成的最长回文串对应节点 int n;//添加的字符数,也是s数组的长度 int p;//节点指针,表示节点数量 int newnode(int x){//创建新节点 for(int i=0;i<26;i++){ nt[p][i]=0; } cnt[p]=num[p]=0; len[p]=x;//长度为x return p++; } void init(){//初始化 p=n=last=0; newnode(0);//偶数长度树 newnode(-1);//奇数长度树 s[n]=-1;//设置第一个字符为-1(也可以是其他不可能被匹配的字符) fail[0]=1; } int getfail(int x){//利用fail找到匹配的节点 while(s[n-len[x]-1]!=s[n])x=fail[x]; return x; } void add(int c){ c-='a'; s[++n]=c; int cur=getfail(last); if(!nt[cur][c]){//如果没有就新建一个节点 int now=newnode(len[cur]+2);//从cur拓展而来 fail[now]=nt[getfail(fail[cur])][c]; nt[cur][c]=now; num[now]=num[fail[now]]+1; } last=nt[cur][c]; cnt[last]++; } void cal(){ for(int i=p-1;i>=0;i--){ cnt[fail[i]]+=cnt[i]; } } }p,pp; char s[maxm],t[maxm]; ll dfs(int a,int b){ ll ans=0; for(int i=0;i<26;i++){ int aa=p.nt[a][i],bb=pp.nt[b][i]; if(aa&&bb){ ans+=(ll)p.cnt[aa]*pp.cnt[bb]+dfs(aa,bb);//注意p.cnt[]是int型的要转long long再相乘 } } return ans; } int main(){ int T; scanf("%d",&T); int cas=1; while(T--){ p.init(); pp.init(); scanf("%s%s",s,t); for(int i=0;s[i];i++){ p.add(s[i]); } for(int i=0;t[i];i++){ pp.add(t[i]); } p.cal(); pp.cal(); ll ans=dfs(0,0)+dfs(1,1); printf("Case #%d: %lld\n",cas++,ans); } return 0; }
来源:51CTO
作者:慢慢搞吧
链接:https://blog.csdn.net/weixin_44178736/article/details/100732319