1. 程式人生 > >WHU 583 Palindrome ( 迴文自動機 && 本質不同的迴文串的個數 )

WHU 583 Palindrome ( 迴文自動機 && 本質不同的迴文串的個數 )

題目連結

題意 : 給你一個串、要你將其劃分成兩個串、使得左邊的串的本質不同迴文子串的個數是右邊串的兩倍、對於每一個這樣子的劃分、其對答案的貢獻就是左邊串的長度、現在要你找出所有這樣子的劃分、並將貢獻乘起來、答案 mod 1e9+7

 

分析 :

從左到右跑一邊迴文自動機、對於每個字首

能夠得出其有多少個本質不同的迴文子串

本質不同的迴文子串的個數實際上就是自動機節點數 - 2

那麼跑一遍字首之後我們能得到所有可作為左邊部分串的本質不同迴文子串的個數

因為是迴文串、所以我們倒著跑一遍、就同樣能得到作為右邊部分串的本質不同迴文子串的個數

最後暴力檢查一遍對於每一個位置是否有符合題意的合理劃分、如果有就將左邊部分長度累乘起來

 

#include<bits/stdc++.h>
#define LL long long
#define ULL unsigned long long

#define scl(i) scanf("%lld", &i)
#define scll(i, j) scanf("%lld %lld", &i, &j)
#define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k)
#define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l)

#define
scs(i) scanf("%s", i) #define sci(i) scanf("%d", &i) #define scd(i) scanf("%lf", &i) #define scIl(i) scanf("%I64d", &i) #define scii(i, j) scanf("%d %d", &i, &j) #define scdd(i, j) scanf("%lf %lf", &i, &j) #define scIll(i, j) scanf("%I64d %I64d", &i, &j) #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k) #define
scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k) #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k) #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l) #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l) #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l) #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 #define lowbit(i) (i & (-i)) #define mem(i, j) memset(i, j, sizeof(i)) #define fir first #define sec second #define VI vector<int> #define ins(i) insert(i) #define pb(i) push_back(i) #define pii pair<int, int> #define VL vector<long long> #define mk(i, j) make_pair(i, j) #define all(i) i.begin(), i.end() #define pll pair<long long, long long> #define _TIME 0 #define _INPUT 0 #define _OUTPUT 0 clock_t START, END; void __stTIME(); void __enTIME(); void __IOPUT(); using namespace std; const int maxn = 400000; const int N = 26 ; struct Palindromic_Tree { int next[maxn][N] ;//next指標,next指標和字典樹類似,指向的串為當前串兩端加上同一個字元構成 int fail[maxn] ;//fail指標,失配後跳轉到fail指標指向的節點 int cnt[maxn] ;//第 i 號節點表示的迴文串出現的次數、注意最後呼叫 count 函式完成計算 int num[maxn] ;//以節點i表示的最長迴文串的最右端點為迴文串結尾的迴文串個數(未經驗證) int len[maxn] ;//len[i]表示節點i表示的迴文串的長度 int S[maxn] ;//存放新增的字元 int last ;//指向上一個字元所在的節點,方便下一次add int n ;//字元陣列指標 int tot ;//節點指標 int newnode ( int l ) {//新建節點 for ( int i = 0 ; i < N ; ++ i ) next[tot][i] = 0 ; cnt[tot] = 0 ; num[tot] = 0 ; len[tot] = l ; return tot ++ ; } void init () {//初始化 tot = 0 ; newnode ( 0 ) ; newnode ( -1 ) ; last = 0 ; n = 0 ; S[n] = -1 ;//開頭放一個字符集中沒有的字元,減少特判 fail[0] = 1 ; } int get_fail ( int x ) {//和KMP一樣,失配後找一個儘量最長的 while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ; return x ; } void add ( int c ) { c -= 'a' ; S[++ n] = c ; int cur = get_fail ( last ) ;//通過上一個迴文串找這個迴文串的匹配位置 if ( !next[cur][c] ) {//如果這個迴文串沒有出現過,說明出現了一個新的本質不同的迴文串 int now = newnode ( len[cur] + 2 ) ;//新建節點 fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自動機一樣建立fail指標,以便失配後跳轉 next[cur][c] = now ; num[now] = num[fail[now]] + 1 ; } last = next[cur][c] ; cnt[last] ++ ; } void count () { for ( int i = tot - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ; //父親累加兒子的cnt,因為如果fail[v]=u,則u一定是v的子迴文串! } }PAM1, PAM2; const LL mod = 1e9 + 7; char str[maxn]; LL L[maxn], R[maxn]; int main(void){__stTIME();__IOPUT(); int T; sci(T); while(T--){ mem(L, 0); mem(R, 0); PAM1.init(); PAM2.init(); scs(str); int len = strlen(str); for(int i=0; i<len; i++){ PAM1.add(str[i]); L[i] = PAM1.tot - 2; } for(int i=len-1; i>=0; i--){ PAM2.add(str[i]); R[i] = PAM2.tot - 2; } LL ans = 0; for(int i=0; i<len; i++){ if(L[i] == 2 * R[i+1]){ if(ans == 0) ans = 1LL; ans = (1LL * (i+1) * ans) % mod; } } printf("%lld\n", ans); } __enTIME();return 0;} void __stTIME() { #if _TIME START = clock(); #endif } void __enTIME() { #if _TIME END = clock(); cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl; #endif } void __IOPUT() { #if _INPUT freopen("in.txt", "r", stdin); #endif #if _OUTPUT freopen("out.txt", "w", stdout); #endif }
View Code