题解 P3449 【[POI2006]PAL-Palindromes】

Baihua

2019-06-24 17:55:02

Solution

### 【题解】[POI2006]PAL-Palindromes * [题目地址](https://www.luogu.org/problemnew/show/P3449) --- 思路: * 回文串和自己相加仍是回文串。 * 当$S[i]$是$S[j]$的前缀时,$S[i]+S[j]$可能构成回文串。 * 对于$S[i]​$,利用$Trie​$获得是$S[i]​$的前缀的字符串。再利用字符串的$Hash​$判断是否回文。 * 知道前串的长度、两串的散列值,就可以获得两串连接后的散列值。如下: * ```c++ HashLink = HashValue[Left] * HashPow[Right.Length] + HashValue[Left] ``` 下面是部分代码(头文件省略)。 * 把字符串加入字典树,并且计算$Hash$值和长度。 ```c++ int Add (int From ) { int P = 0 ,R = 1 ; unsigned long long int NowHash = 0 , NowLen = 0; for (int i = From ; S[i] != '0' ; ++ i) { if (!T[P][S[i] - 'a']) T[P][S[i] - 'a'] = ++ Tot; P = T[P][S[i] - 'a'] ; ++ R; NowHash = NowHash * HashBase + S[i] ; ++ NowLen ; } ++ End[P] ; Len[P] = NowLen , HashValue[P] = NowHash ; return From + R ; } ``` * 对于每一个字符串,枚举它的前缀,并且判断$Hash(Left + Right) == Hash(Right + Left)$。如果成立,那将对答案贡献2。 * ```c++ void Ask (int P , int Now , int E ) { if (Now > E) { NowP = P ; return ; } Ask (T[P][S[Now] - 'a'] , Now + 1 , E ) ; if (End[P]) { if (HashValue[NowP] * HashPow[Len[P]] + HashValue[P] == HashValue[P] * HashPow[Len[NowP]] + HashValue[NowP]) { Ans += 2 ; if (P == NowP) -- Ans ; } } } ``` * 最后,每个字符串和自己可以连接。 * 完整代码: ```c++ #include <stdio.h> #include <string.h> #define Clean(X,K) memset(X,K,sizeof(X)) using namespace std ; #include <iostream> const int Maxn = 2000005 , Base = 26 ; int N , Tail = 0 , T[Maxn][Base] , End[Maxn] , Tot = 0 , Len[Maxn]; unsigned long long HashValue[Maxn] , HashBase = 17 , HashPow[Maxn] = {1} , Ans = 0; char S[Maxn * 2] , Tmp[Maxn]; int Add (int From ) { int P = 0 ,R = 1 ; unsigned long long int NowHash = 0 , NowLen = 0; for (int i = From ; S[i] != '0' ; ++ i) { if (!T[P][S[i] - 'a']) T[P][S[i] - 'a'] = ++ Tot; P = T[P][S[i] - 'a'] ; ++ R; NowHash = NowHash * HashBase + S[i] ; ++ NowLen ; } ++ End[P] ; Len[P] = NowLen , HashValue[P] = NowHash ; return From + R ; } int NowP ; void Ask (int P , int Now , int E ) { if (Now > E) { NowP = P ; return ; } Ask (T[P][S[Now] - 'a'] , Now + 1 , E ) ; if (End[P]) { if (HashValue[NowP] * HashPow[Len[P]] + HashValue[P] == HashValue[P] * HashPow[Len[NowP]] + HashValue[NowP]) { Ans += 2 ; if (P == NowP) -- Ans ; } } } int main () { // freopen ("P3449.in" , "r" , stdin) ; Clean (T , 0) , Clean (End , 0) , Clean (Len , 0); scanf ("%d" , &N) ; for (int i = 1 ; i <= N; ++ i) { int L ; scanf ("%d " , &L) ; scanf ("%s" , Tmp) ; for (int j = 0 ; j < L; ++ j) S[Tail + j] = Tmp[j] ; Tail += L ; S[Tail++] = '0' ; } -- Tail ; int Now = 0 , Cnt = -1; while (Now <= Tail) Now = Add (Now) ; for (int i = 1 ; i < Maxn ; ++ i) HashPow[i] = HashPow[i - 1] * HashBase ; int Lst = 0 ; Cnt = -1 ; for (int i = 1 ; i <= Tail ; ++ i) if (S[i] == '0') { Ask (0 , Lst , i - 1) ; Lst = i + 1 ; } cout << Ans + N<<endl; fclose (stdin) , fclose (stdout) ; return 0 ; } ```