HDU 5421(回文树)

传送门

题面:

Victor loves to play with string. He thinks a string is charming as the string is a palindromic string. 

Victor wants to play nn times. Each time he will do one of following four operations.

Operation 1 : add a char cc to the beginning of the string. 

Operation 2 : add a char cc to the end of the string. 

Operation 3 : ask the number of different charming substrings. 

Operation 4 : ask the number of charming substrings, the same substrings which starts in different location has to be counted. 

At the beginning, Victor has an empty string.

Input

The input contains several test cases, at most 55 cases. 

In each case, the first line has one integer nn means the number of operations. 

The first number of next nn line is the integer opop, meaning the type of operation. If opop=11 or 22, there will be a lowercase English letters followed. 

1≤n≤1000001≤n≤100000.

Output

For each query operation(operation 3 or 4), print the correct answer.

Sample Input

6
1 a
1 b
2 a
2 c
3
4
8
1 a
2 a
2 a
1 a
3
1 b
3
4

Sample Output

4
5
4
5
11

题意:

    给你n次操作,如果为1,则在字符串后面插入一个字符,如果为2,则在字符串前面插入一个字符,如果为3,则输出当前的字符串中的本质不同的回文串的个数,如果为4,则输出字符串的回文串的个数。

题目分析:

    拿到这个问题,不难想到一个最简单的算法:用string加前后字符串,并不断建回文树去计算答案。

    但是很显然,这样做的时间复杂度大约在O(n^2)的样子,显然不满足题意。

    因此这个题我们则需要动态地建立回文树。

    考虑到是要在前面插入字符,我们考虑开两个last分别记录两端的端点。

    我们用last[0]表示首部的操作的位置,last[1]表示尾部的操作的位置。

    last[0]的操作即为之前的常规操作,而新的last[1]我们只需要维护当前字串的最长公共前缀,在找fail指针的时候也不断找最长公共前缀即可,剩下的操作跟last[0]即为类似。

    最后需要注意,倘若我们发现当前结点的回文子串的长度是整个字符串,则我们需要将另外一种方向的last指针也指向该结点。(证明另外一种方向也可以从此结点开始拓展)

代码:

#include <bits/stdc++.h>
#define maxn 200005
using namespace std;
typedef long long ll;
struct PAM{
    int next[maxn][26],fail[maxn],S[maxn],num[maxn],len[maxn];
    int id,last[2],n;
    int rb,lb;
    int newnode(int x){
        for(int i=0;i<26;i++) next[id][i]=0;
        len[id]=x;
        num[id]=0;
        return id++;
    }
    void init(int x){
        id=0;
        newnode(0);
        newnode(-1);
        memset(S,-1,sizeof(S));
        lb=x,rb=x-1;
        last[0]=last[1]=n=0;
        fail[0]=1;
    }
    int getfail(int x,int d){
        if(d==0) while(S[lb+len[x]+1]!=S[lb]) x=fail[x];
        else while(S[rb-len[x]-1]!=S[rb]) x=fail[x];
        return x;
    }
    int Insert(int c,int d){
        if(d==0) S[--lb]=c;
        else S[++rb]=c;
        int cur=getfail(last[d],d);
        if(!next[cur][c]){
            int now=newnode(len[cur]+2);
            fail[now]=next[getfail(fail[cur],d)][c];
            num[now]=num[fail[now]]+1;
            next[cur][c]=now;
        }
        last[d]=next[cur][c];
        if(len[last[d]]==rb-lb+1) last[d^1]=last[d];//将另一种情况的last也指向该结点
        return num[last[d]];
    }
}pam;
int main()
{
    int n;
    while(~scanf("%d",&n)){
        ll ans=0;
        pam.init(n);
        for(int i=0;i<n;i++){
            int op;
            char ch;
            scanf("%d",&op);
            if(op==1){
                scanf(" %c",&ch);
                ans+=pam.Insert(ch-'a',0);
            }
            else if(op==2){
                scanf(" %c",&ch);
                ans+=pam.Insert(ch-'a',1);
            }
            else if(op==3){
                printf("%d
",pam.id-2);
            }
            else{
                cout<<ans<<endl;
            }
        }
    }

}
原文地址:https://www.cnblogs.com/Chen-Jr/p/11007206.html