1. 程式人生 > >Gym - 101806Q:QueryreuQ(第一次寫回文樹)

Gym - 101806Q:QueryreuQ(第一次寫回文樹)

str ase integer oid index war itl ppa like

A string is palindrome, if the string reads the same backward and forward. For example, strings like "a", "aa", "appa", "queryreuq" are all palindromes.

For given empty string S, you should process following two queries :

  1. Add a lower case alphabet at the back of S.
  2. Remove a character at the back of S
    .

After processing a query, you should count the number of palindrome substring in S. For string S and integers i,  j (1 ≤ i ≤ j ≤ |S|), 技術分享圖片 represents a substring from ith to jth character of S. You should print out the number of integer pairs (i

,  j) where 技術分享圖片 is palindrome.


Input

Input consists of two lines.

In the first line, Q, the number of queries is given. (1 ≤ Q ≤ 10, 000)

In the second line, the query is given as string of length Q. ith character Ki denotes the ith query.

Ki is ‘-‘ or lower case alphabet (‘a‘, ‘b‘, ..., ‘z‘) (without quotes).

If the character is ‘-‘, you should remove a character at the back of S. If the character is lower case alphabet, you should add a character Ki at the back of S.

It is guaranteed that length of S is always positive after the query.

Output

Print out Q space-separated integers in the first line. i-th integer should be the answer of the ith query.

Example Input
17
qu-uer-ryr-reu-uq
Output
1 2 1 2 3 4 3 4 5 7 5 7 9 11 9 11 13 

題意:現在有一個空的字符串S,S每次在末尾添加一個字符,或者刪去末尾的字符(輸入‘-‘號表示)。現在讓你求每次操作後字符串S的回文串個數。

思路:mad,隊友寫了一發回文樹,但是他是每次操作都暴力建樹,暴力求的,所以為了在線求而不是每次重新建樹,我還現學了一下回文樹,挺簡單的。

就是每次fail指針跑就完事了。 比後綴自動機好理解多了。 那麽這個題,每次我們加入一個字符,那就加到回文樹裏,如果刪去,則在fail鏈上的所有點都-1,如果-1後變為0,則刪去那個點(sz標記為-1)。

但是在全部都是aaaaaaa...這種情況下,復雜度還是有點高,和暴力的復雜度差不多,復雜度小於(N^2)/4,不過N為10000可以過。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=10010;
char c[maxn],s[maxn]; int fcy[maxn],pos[maxn];
struct Palindromic_Tree
{
    struct Node
    {
        int son[26];
        int fail,len;
    }t[maxn];
    int last,tot,sz[maxn];
    void init()
    {
        memset(sz,-1,sizeof(sz));
        t[++tot].len=-1; //-1是因為後面找fail是時候可以break
        t[0].fail=t[1].fail=1; //奇偶兩棵樹
    }
    int del(int Now)
    {
        int res=0;
        while(Now>1){
            if(sz[Now]==-1) continue;
            if(sz[Now]==1) sz[Now]=-1;
            else sz[Now]--;
            res++; Now=t[Now].fail;
        } return res;
    }
    void add(int c,int n)
    {
        int p=pos[n-1];
        while(s[n-t[p].len-1]!=s[n]) p=t[p].fail;
        if(!t[p].son[c])
        {
            int v=++tot,k=t[p].fail;
            t[v].len=t[p].len+2;
            while(s[n-t[k].len-1]!=s[n]) k=t[k].fail;
            t[v].fail=t[k].son[c];
            t[p].son[c]=v;
            sz[v]=0;
        }
        last=t[p].son[c];
        int Now=last,res=0;
        while(Now>1){
            if(sz[Now]!=-1) sz[Now]++,res++;
            else sz[Now]=1,res++;
            Now=t[Now].fail;
        }
        fcy[n]=res; pos[n]=last;
    }
}T;
int main()
{
    T.init(); int N,L=0,ans=0;
    scanf("%d%s",&N,c+1);
    pos[0]=1;
    rep(i,1,N){
        if(c[i]==-){
            ans-=T.del(pos[L]); L--;
        }
        else {
            L++; s[L]=c[i];
            T.add(c[i]-a,L);
            ans+=fcy[L];
        }
        printf("%d ",ans);
    }
    return 0;
}

Gym - 101806Q:QueryreuQ(第一次寫回文樹)