回文树练习题

版权声明:By MrBird https://blog.csdn.net/MrBird_to_fly/article/details/82531937

贴(改)模板大集合系列
大概包括了pam的这些问题:
1.sz的意义:本质不同的回文子串个数
2.cnt的意义:当前回文子串出现的次数
3.num的意义:靠最右边的回文子串个数、新加一个字符产生的回文子串个数、暴力fail的层数
4.half的意义:长度小于等于当前回文子串的一半的回文后缀的节点
5.支持两边加字符的回文自动机的做法:维护一左一右两个last
大致就这些

Tsinsen A1280
问题描述
  顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。
  输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100007;
char s[maxn];
struct PAM{
    int next[maxn][26];
    int fail[maxn];
    int cnt[maxn];
    int len[maxn];
    int S[maxn];
    int last,n,sz;
    int newnode(int l){
        for(int i=0;i<26;i++){
            next[sz][i]=0;
        }
        cnt[sz]=0;
        len[sz]=l;
        return sz++;
    }
    void init(){
        sz=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;
    }
    void insert(char ch){
        int c=ch-'a';
        S[++n]=c;
        int u=getfail(last);
        if(!next[u][c]){
            int v=newnode(len[u]+2);
            fail[v]=next[getfail(fail[u])][c];
            next[u][c]=v;
        }
        last=next[u][c];
        cnt[last]++;
    }
    void count(){
        for(int i=sz-1;i;i--){
            cnt[fail[i]]+=cnt[i];
        }
    }
}pam;
int pre[maxn];
int main(){
    int ans=0;
    scanf("%s",s);
    int n=strlen(s);
    pam.init();
    for(int i=0;i<n;i++){
        pam.insert(s[i]);
        pre[i]=pam.len[pam.last];
    }
    pam.init();
    for(int i=n-1;i>0;i--){
        pam.insert(s[i]);
        ans=max(ans,pre[i-1]+pam.len[pam.last]);
    }
    printf("%lld\n",ans);
}

Tsinsen A1393
问题描述
  给你一个长度 n (1 ≤ n ≤ 2·106) 的只由小写字母组成的字符串s。
  我们考虑s的所有连续且回文的子串集合P。位置不同但内容相同的两个串算作不同。
  问从P中选出两个串且他们在s中有公共位置的方法数有几个?

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=51123987;
const int inv2=(mod+1)/2;
const int maxn=2000007;
char s[maxn];
struct PAM{
    int next[maxn][26];
    int fail[maxn];
    int cnt[maxn];
    int num[maxn];
    int len[maxn];
    int S[maxn];
    int last,n,sz;
    int newnode(int l){
        for(int i=0;i<26;i++){
            next[sz][i]=0;
        }
        cnt[sz]=0;
        len[sz]=l;
        return sz++;
    }
    void init(){
        sz=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;
    }
    void insert(char ch){
        int c=ch-'a';
        S[++n]=c;
        int u=getfail(last);
        if(!next[u][c]){
            int v=newnode(len[u]+2);
            fail[v]=next[getfail(fail[u])][c];
            next[u][c]=v;
            num[v]=num[fail[v]]+1;
        }
        last=next[u][c];
        cnt[last]++;
    }
    ll count(){
        ll ret=0;
        for(int i=sz-1;i;i--){
            cnt[fail[i]]+=cnt[i];
            ret=(ret+cnt[i])%mod;
        }
        return ret;
    }
}pam;
ll suf[maxn];
int main(){
    int ans=0;
    int n;
    scanf("%d",&n);
    scanf("%s",s);
    pam.init();
    for(int i=n-1;i>=0;i--){
        pam.insert(s[i]);
        suf[i]=(suf[i+1]+pam.num[pam.last])%mod;
    }
    ll tot=suf[0]*(suf[0]-1+mod)%mod*inv2%mod;
    pam.init();
    ll rev=0;
    for(int i=0;i<n;i++){
        pam.insert(s[i]);
        rev=(rev+pam.num[pam.last]*suf[i+1])%mod;
    }
    printf("%lld\n",(tot-rev+mod)%mod);
}

ACM-ICPC 2018 南京赛区网络预赛 I.Skr
问题描述
求本质不同回文子串的hash和

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=2000007;
char s[maxn];
ll pw[maxn];
ll ans=0;
struct PAM{
    int next[maxn][10];
    int fail[maxn];
    int cnt[maxn];
    int len[maxn];
    ll val[maxn];
    int S[maxn];
    int last,n,sz;
    int newnode(int l){
        for(int i=0;i<10;i++){
            next[sz][i]=0;
        }
        cnt[sz]=0;
        len[sz]=l;
        val[sz]=0;
        return sz++;
    }
    void init(){
        sz=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;
    }
    void insert(char ch){
        int c=ch-'0';
        S[++n]=c;
        int u=getfail(last);
        if(!next[u][c]){
            int v=newnode(len[u]+2);
            if(len[u]==-1){
                val[v]=c;
            }
            else{
                val[v]=(val[u]*10+c+c*pw[len[u]+1])%mod;
            }
            ans=(ans+val[v])%mod;
            fail[v]=next[getfail(fail[u])][c];
            next[u][c]=v;
        }
        last=next[u][c];
        cnt[last]++;
    }
    void count(){
        for(int i=sz-1;i;i--){
            cnt[fail[i]]+=cnt[i];
        }
    }
}pam;
int main(){
    scanf("%s",s);
    pam.init();
    pw[0]=1;
    for(int i=1;i<maxn;i++)pw[i]=pw[i-1]*10%mod;
    for(int i=0;s[i];i++){
        pam.insert(s[i]);
    }
    printf("%lld\n",ans);
}

Bzoj 2342
问题描述
求最长双倍回文偶回文子串长度

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=500007;
char s[maxn];
struct edge{
    int to,next;
}e[maxn];
int head[maxn];
int cnt;
void init(){
    cnt=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v){
    e[cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
struct PAM{
    int next[maxn][26];
    int fail[maxn];
    int cnt[maxn];
    int num[maxn];
    int len[maxn];
    int S[maxn];
    int last,n,sz;
    int newnode(int l){
        for(int i=0;i<26;i++){
            next[sz][i]=0;
        }
        cnt[sz]=0;
        len[sz]=l;
        return sz++;
    }
    void init(){
        sz=0;
        newnode(0);
        newnode(-1);
        last=0;
        n=0;
        S[n]=-1;
        fail[0]=1;
        addedge(1,0);
    }
    int getfail(int x){
        while(S[n-len[x]-1]!=S[n])x=fail[x];
        return x;
    }
    void insert(char ch){
        int c=ch-'a';
        S[++n]=c;
        int u=getfail(last);
        if(!next[u][c]){
            int v=newnode(len[u]+2);
            fail[v]=next[getfail(fail[u])][c];
            addedge(fail[v],v);
            next[u][c]=v;
            num[v]=num[fail[v]]+1;
        }
        last=next[u][c];
        cnt[last]++;
    }
    void count(){
        for(int i=sz-1;i;i--){
            cnt[fail[i]]+=cnt[i];
        }
    }
}pam;
int vis[maxn];
int ans;
void dfs(int u){
    if(pam.len[u]%4==0&&vis[pam.len[u]/2]){
        ans=max(ans,pam.len[u]);
    }
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        vis[pam.len[v]]++;
        dfs(v);
        vis[pam.len[v]]--;
    }
}
int main(){
    int n;
    scanf("%d",&n);
    scanf("%s",s);
    init();
    pam.init();
    for(int i=0;i<n;i++){
        pam.insert(s[i]);
    }
    ans=0;
    dfs(1);
    printf("%d\n",ans);
}

Hdu 5421
问题描述
维护一个字符串,支持4种操作
1. 左增加一个字符
2. 右增加一个字符
3. 查询本质不同的回文子串个数
4. 查询回文子串个数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=200007;
typedef long long ll;
struct PAM{
    int next[maxn][26];
    int fail[maxn];
    int cnt[maxn];
    int num[maxn];
    int len[maxn];
    int S[maxn];
    int last1,last2,l,r,sz;
    int newnode(int l){
        for(int i=0;i<26;i++){
            next[sz][i]=0;
        }
        cnt[sz]=0;
        len[sz]=l;
        return sz++;
    }
    void init(){
        sz=0;
        newnode(0);
        newnode(-1);
        last1=last2=0;
        l=100002;
        r=l-1;
        memset(S,-1,sizeof(S));
        fail[0]=1;
    }
    int getfail1(int x){
        while(S[l+len[x]+1]!=S[l])x=fail[x];
        return x;
    }
    int getfail2(int x){
        while(S[r-len[x]-1]!=S[r])x=fail[x];
        return x;
    }
    void insertbefore(char ch){
        int c=ch-'a';
        S[--l]=c;
        int u=getfail1(last1);
        if(!next[u][c]){
            int v=newnode(len[u]+2);
            fail[v]=next[getfail1(fail[u])][c];
            next[u][c]=v;
            num[v]=num[fail[v]]+1;
        }
        last1=next[u][c];
        cnt[last1]++;
        if(len[last1]==r-l+1)last2=last1;
    }
    void insertafter(char ch){
        int c=ch-'a';
        S[++r]=c;
        int u=getfail2(last2);
        if(!next[u][c]){
            int v=newnode(len[u]+2);
            fail[v]=next[getfail2(fail[u])][c];
            next[u][c]=v;
            num[v]=num[fail[v]]+1;
        }
        last2=next[u][c];
        cnt[last2]++;
        if(len[last2]==r-l+1)last1=last2;
    }
    void count(){
        for(int i=sz-1;i;i--){
            cnt[fail[i]]+=cnt[i];
        }
    }
}pam;
int main(){
    int n;
    while(~scanf("%d",&n)){
        pam.init();
        ll sum=0;
        for(int i=1;i<=n;i++){
            int op;
            char str[3];
            scanf("%d",&op);
            if(op==1){
                scanf("%s",str);
                pam.insertbefore(str[0]);
                sum+=pam.num[pam.last1];
            }
            else if(op==2){
                scanf("%s",str);
                pam.insertafter(str[0]);
                sum+=pam.num[pam.last2];
            }
            else if(op==3){
                printf("%d\n",pam.sz-2);
            }
            else {
                printf("%lld\n",sum);
            }
        }
    }
}

CF 245H
题目描述
给一个长为5000的字符串,多次查询区间回文子串个数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5007;
char s[maxn];
struct PAM{
    int next[maxn][26];
    int fail[maxn];
    int cnt[maxn];
    int num[maxn];
    int len[maxn];
    int S[maxn];
    int last,n,sz;
    int newnode(int l){
        for(int i=0;i<26;i++){
            next[sz][i]=0;
        }
        cnt[sz]=0;
        len[sz]=l;
        return sz++;
    }
    void init(){
        sz=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;
    }
    void insert(char ch){
        int c=ch-'a';
        S[++n]=c;
        int u=getfail(last);
        if(!next[u][c]){
            int v=newnode(len[u]+2);
            fail[v]=next[getfail(fail[u])][c];
            next[u][c]=v;
            num[v]=num[fail[v]]+1;
        }
        last=next[u][c];
        cnt[last]++;
    }
    void count(){
        for(int i=sz-1;i;i--){
            cnt[fail[i]]+=cnt[i];
        }
    }
}pam;
int n;
int ans[5003][5003];
int main(){
    scanf("%s",s);
    n=strlen(s);
    for(int l=0;l<n;l++){
        pam.init();
        int sum=0;
        for(int r=l;r<n;r++){
            pam.insert(s[r]);
            sum+=pam.num[pam.last];
            ans[l][r]=sum;
        }
    }
    int q;
    scanf("%d",&q);
    for(int i=0;i<q;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%d\n",ans[l-1][r-1]);
    }
}

CodeChef Palindromeness
题目描述
每个字符串的价值定义如下:
1. 非回文串价值为0
2. 若回文串S长度为len,则V(S)=V(S[0…len/2-1])+1
求给定字符串所有子串的价值和

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100007;
char s[maxn];
struct PAM{
    int next[maxn][26];
    int fail[maxn];
    int cnt[maxn];
    int num[maxn];
    int len[maxn];
    int dp[maxn];
    int sum[maxn];
    int S[maxn];
    int half[maxn];
    int last,n,sz;
    int newnode(int l){
        for(int i=0;i<26;i++){
            next[sz][i]=0;
        }
        cnt[sz]=0;
        len[sz]=l;
        dp[sz]=0;
        sum[sz]=0;
        return sz++;
    }
    void init(){
        sz=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 gethalf(int x,int l){
        while(S[n-len[x]-1]!=S[n]||len[x]+2>l)x=fail[x];
        return x;
    }
    void insert(char ch){
        int c=ch-'a';
        S[++n]=c;
        int u=getfail(last);
        if(!next[u][c]){
            int v=newnode(len[u]+2);
            fail[v]=next[getfail(fail[u])][c];
            next[u][c]=v;
            num[v]=num[fail[v]]+1;
            if(len[v]==1)half[v]=1;
            else half[v]=next[gethalf(half[u],len[v]/2)][c];
            if(len[half[v]]==len[v]/2)dp[v]=dp[half[v]]+1;
            else dp[v]=1;
            sum[v]=dp[v]+sum[fail[v]];
        }
        last=next[u][c];
        cnt[last]++;
    }
    void count(){
        for(int i=sz-1;i;i--){
            cnt[fail[i]]+=cnt[i];
        }
    }
}pam;
ll ans;
int n;
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",s);
        n=strlen(s);
        pam.init();
        ans=0;
        for(int i=0;i<n;i++){
            pam.insert(s[i]);
            ans+=pam.sum[pam.last];
        }
        printf("%lld\n",ans);
    }
}

Aizu 2292
题目描述
求两个字符串的公共回文子串对数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100007;
char s1[maxn],s2[maxn];
struct PAM{
    int next[maxn][26];
    int fail[maxn];
    int cnt[maxn];
    int num[maxn];
    int len[maxn];
    int S[maxn];
    int last,n,sz;
    int newnode(int l){
        for(int i=0;i<26;i++){
            next[sz][i]=0;
        }
        cnt[sz]=0;
        len[sz]=l;
        return sz++;
    }
    void init(){
        sz=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;
    }
    void insert(char ch){
        int c=ch-'A';
        S[++n]=c;
        int u=getfail(last);
        if(!next[u][c]){
            int v=newnode(len[u]+2);
            fail[v]=next[getfail(fail[u])][c];
            next[u][c]=v;
            num[v]=num[fail[v]]+1;
        }
        last=next[u][c];
        cnt[last]++;
    }
    void count(){
        for(int i=sz-1;i;i--){
            cnt[fail[i]]+=cnt[i];
        }
    }
}pam1,pam2;
ll ans;
int n,m;
void dfs(int u1,int u2){
    if(u1>1)ans+=1ll*pam1.cnt[u1]*pam2.cnt[u2];
    for(int i=0;i<26;i++){
        int v1=pam1.next[u1][i];
        int v2=pam2.next[u2][i];
        if(v1&&v2)dfs(v1,v2);
    }
}
int main(){
    scanf("%s%s",s1,s2);
    n=strlen(s1);
    m=strlen(s2);
    pam1.init();
    pam2.init();
    for(int i=0;i<n;i++){
        pam1.insert(s1[i]);
    }
    pam1.count();
    for(int i=0;i<m;i++){
        pam2.insert(s2[i]);
    }
    pam2.count();
    ans=0;
    dfs(0,0);
    dfs(1,1);
    printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/MrBird_to_fly/article/details/82531937