1. 程式人生 > >ACM-ICPC 2018南京賽區網路賽 I.Skr (迴文自動機)

ACM-ICPC 2018南京賽區網路賽 I.Skr (迴文自動機)

計蒜客開了禁止複製,我掛了破解網頁限制的指令碼能複製但是不太好看,就不貼題目了。

題目大意是給出一個數字串,求其本質不同的迴文子串的和。

一開始沒看到“本質不同”,上來就想跑馬拉車 + Sam,重新讀題後撲街。

後來知道是迴文樹的題,學完了迴文樹才補。

在迴文樹建立的過程中自帶去重,所以只需要跑一遍記錄答案就好了。

奇根下直接連線的節點所代表的的都是單個字元的迴文串,其他都是在兩邊加上同一個字元,用這個規律去生成數字求和就好了。

ac程式碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long
ll; const int maxn = 2000005; const int mod = 1000000007; char s[maxn]; ll modPow(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) { ans = (ans * a) % mod; } b >>= 1; a = (a * a) % mod; } return ans; } struct Pam { int next[maxn << 1
][9]; int fail[maxn << 1]; int len[maxn << 1]; // 記錄數字 ll num[maxn << 1]; ll ans; int S[maxn << 1]; int last, n, p; int newNode(int l) { memset(next[p], 0, sizeof(next[p])); len[p] = l; return p++; } void init() { n = last = p = 0
; newNode(0); newNode(-1); S[n] = -1; fail[0] = 1; } int getFail(int x) { while(S[n - len[x] - 1] != S[n]) { x = fail[x]; } return x; } void add(int c) { S[++n] = c; int cur = getFail(last); if(!next[cur][c]) { int now = newNode(len[cur] + 2); fail[now] = next[getFail(fail[cur])][c]; next[cur][c] = now; // len[cur] = -1時,是單個字元組成的迴文串,特判 if(len[cur] == -1) { num[now] = c; } else { num[now] = (((num[cur] * 10) % mod + c) % mod + (c * modPow(10, len[now] - 1)) % mod) % mod; } ans = (ans + num[now]) % mod; } last = next[cur][c]; } void solve() { init(); for(int i = 0, len = strlen(s); i < len; i++) { add(s[i] - '0'); } printf("%lld\n", ans); } } pam; int main() { scanf("%s", s); pam.solve(); return 0; }