1. 程式人生 > >HDU 5421(迴文樹)

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\n",pam.id-2);
            }
            else{
                cout<<ans<<endl;
            }
        }
    }

}