Common Palindromes Aizu - 2292

题意:给定S,T,询问有多少(l1,r1,l2,r2)使得S[l1,r1]回文且S[l1,r1]=T[l2,r2]。

先对S建一颗回文树,额外开一个cntt[i]数组记录t串和s串的i节点回文串的匹配个数,然后在拿T串在S的回文树上跑,这里只是跑,并不对树做任何修改,因为新开的节点对应回文串在S串里必定没有出现,而我们只需要t串和s串匹配的回文串。

#include<bits/stdc++.h>
using namespace std;
#define ls rt<<1
#define rs (rt<<1)+1
#define ll long long
#define fuck(x) cout<<#x<<"     "<<x<<endl;
const int maxn=50000+10;
const ll mod=51123987;
typedef pair<char,int> pci;
typedef pair<int,int>pii;
int d[4][2]={1,0,-1,0,0,1,0,-1};
char s[maxn];
const int MAXN =50000+10;//长度
const int N = 26;//字符集大小
struct Palindromic_Tree {
    int next[MAXN][N];//next指针,next指针和字典树类似,指向的回文子串为i节点对应的回文子串两端加上同一个字符ch构成
    int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点,fail指针指向的是i节点对应的回文子串的最长后缀回文子串(是真后缀),这个匹配过程与kmp有点类似,fail[i]表示节点i失配以后跳转到长度小于该串且以该节点表示回文串的最后一个字符结尾的最长回文串表示的节点
    int cnt[MAXN];//在调用count函数之后,cnt[i]表示i节点对应的回文子串的出现次数的准确值
    int num[MAXN];//在调用add函数之后返回num[last]可以得到以i位置的字符为尾的回文串个数
    int len[MAXN];//len[i]表示节点i表示的回文串的长度
    int S[MAXN];//存放添加的字符,
    int last;//指向上一个字符所在的节点,方便下一次add
    int n;//字符数组指针,从1开始,到n结束
    int p;//节点指针,0指向偶根,1指向奇根,有效编号到p-1

    int newnode(int l) {//新建节点
        for (int i = 0; i < N; ++i) next[p][i] = 0;
        cnt[p] = 0;
        num[p] = 0;
        len[p] = l;
        fail[p]=0;
        return p++;
    }

    void init() {//初始化
        p = 0;
        newnode(0);
        newnode(-1);
        last = 0;
        n = 0;
        S[n] = -1;//开头放一个字符集中没有的字符,减少特判
        fail[0] = 1;
    }

    int get_fail(int x) {//和KMP一样,失配后找一个尽量最长的
        while (S[n - len[x] - 1] != S[n]) x = fail[x];
        return x;
    }

    int add(int c) {
        c -= 'A';
        S[++n] = c;
        int cur = get_fail(last);//通过上一个回文串找这个回文串的匹配位置
        if (!next[cur][c]) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
            int now = newnode(len[cur] + 2);//新建节点
            fail[now] = next[get_fail(fail[cur])][c];//和AC自动机一样建立fail指针,以便失配后跳转
            next[cur][c] = now;
            num[now] = num[fail[now]] + 1;
        }
        last = next[cur][c];
        cnt[last]++;
        return num[last];
    }

    void count() {
        for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];
        //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
    }
}pt;

ll cntt[maxn];
int last;
int main(){
    int len;
    ll ans=0;
    scanf("%s",s+1);
    len=strlen(s+1);
    pt.init();
    for (int i = 1; i <= len; i++)
        pt.add(s[i]);
    pt.count();
    scanf("%s",s+1);
    len=strlen(s+1);
    last=1;
    for (int i = 1; i<=len; i++)
    {
        int c=s[i]-'A';
        while(last!=1&&(!pt.next[last][c]||s[i]!=s[i-pt.len[last]-1])) last=pt.fail[last];
        if(pt.next[last][c]&&s[i]==s[i-pt.len[last]-1]) last=pt.next[last][c],cntt[last]++;
    }
    for(int i=pt.p-1;i>=1;i--) cntt[pt.fail[i]]+=cntt[i];
    for(int i=1;i<=pt.p-1;i++) ans+=pt.cnt[i]*cntt[i];
    cout<<ans<<endl;
    return 0;
}

 

posted @ 2019-08-08 09:56  eason99  阅读(73)  评论(0编辑  收藏  举报