WHU 1583 Palindrome(迴文樹)

題意:把一個串分成2部分,求左半部份的本質不同迴文串個數等於右半部份本質不同迴文串個數的2倍的所有位置乘起來的答案。

做法:學習了下回文樹,總結幾點。

1.用編號去代表每個不同的迴文串。比如編號爲5的迴文串爲aba,那麼next[5]['z']就是zabaz。next[i][j]保存的是這個在編號爲i迴文串的兩端加字母j的迴文串的編號。

2.fail[i]代表編號爲i的迴文串的包含在內具有相同右端點的最長迴文串。。例如ababa,下一個是aba,再下一個是a。

3.類似ac自動機的失配做法。通過fail嘗試所有以前一個字母爲右端點的迴文串,例如長度是L,那麼就判斷當前位置pos-1-L是否與pos字母相同。相同就匹配成功。不相同就繼續fail,匹配成功即是以當前位置爲右端點的最長的迴文串。

4.在求出最長迴文串後得看看是否是新的,那麼這個就要利用到next,例如當前字母爲a,匹配成功的前一個迴文串編號爲last,那麼就要看next[last]['a']是否不爲0,爲0說明當前串是新串(那麼就要加入),反之就是已經出現過。

5.如果是新串就需要求fail指針,是爲了後面一個加入字母的匹配。我們繼續類似3的方法,繼續fail[last],求是否匹配。即求下一個以當前位置爲右端點的迴文串。注意這裏找到的迴文串必定已經出現過了。因爲當前的串爲迴文串,那麼找到的通過對稱可以發覺左邊已經出現過了。所以可以用fail[i] = next[last][a]。

6.需要設定邊界。即偶數邊界和奇數邊界,偶數邊界的len爲0,奇數邊界的len爲-1,這都是因爲pos-1-L所決定的。偶數邊界就是與前一個比較,奇數邊界就是與自己比較(必定能匹配啦)。

網上對比了一些代碼。刪去了一些無用的語句。感覺比較簡潔易懂了。

AC代碼:

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<string.h>
#include<string>
#include<sstream>
#include<bitset>
using namespace std;
#define ll long long
#define ull unsigned long long
#define eps 1e-8
#define MAXN 100000+100
#define MOD 1000000007
#define palin(x, id, c) (s[id - 1 - l[x]] - 'a' == c)

char str[MAXN],str2[MAXN];
struct PalindromeTree
{
    int sz, odd_, even, last;
    int next[MAXN][26], fail[MAXN], l[MAXN];
    void Init(char *s)
    {
        sz = 0;
        odd_ = ++sz, l[odd_] = -1, fail[odd_] = odd_;
        memset(next[sz],0,sizeof(next[sz]));
        even = ++sz, l[even] =  0, fail[even] = odd_;
        memset(next[sz],0,sizeof(next[sz]));
        last = odd_;
        s[0] = '$';
    }
    void Add(int c, int id, char *s)
    {
        while(!palin(last, id, c)) last = fail[last];
        if (next[last][c]) last = next[last][c];
        else
        {
            int x = last;
            ++sz;
            memset(next[sz],0,sizeof(next[sz]));
            next[x][c] = sz, l[sz] = l[x] + 2;
            if (x == odd_) fail[sz] = even;
            else
            {
                x = fail[x];
                while(!palin(x, id, c)) x = fail[x];
                fail[sz] = next[x][c];
            }
            last = sz;
        }
    }
}pt;
int ans1[MAXN],ans2[MAXN];
int main()
{
#ifdef GLQ
    freopen("input.txt","r",stdin);
//    freopen("o.txt","w",stdout);
#endif
    int cas = 1,t;
    scanf("%d\n",&t);
    while(t--)
    {
        gets(str+1);
        pt.Init(str);
        int len = strlen(str+1);
        for(int i = 1; i <= len; i++)
        {
            pt.Add(str[i] - 'a', i, str);
            ans1[i] = pt.sz-2;
            str2[len-i+1] = str[i];
        }
        pt.Init(str2);
        for(int i = 1; i <= len; i++)
        {
            pt.Add(str2[i] - 'a', i, str2);
            ans2[len-i+1] = pt.sz-2;
        }
        ll ret = 1;
        for(int i = 1; i <= len-1; i++)
        {
//            cout<<i<<" "<<ans1[i]<<" "<<ans2[i+1]<<endl;
            if(ans1[i] == ans2[i+1]*2) ret = ret*(ll)i%MOD;
        }
        if(ret == 1) ret = 0;
        printf("%lld\n",ret);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章