ACM-ICPC 2018南京賽區網路賽 I.Skr (迴文自動機)
阿新 • • 發佈:2018-12-09
計蒜客開了禁止複製,我掛了破解網頁限制的指令碼能複製但是不太好看,就不貼題目了。
題目大意是給出一個數字串,求其本質不同的迴文子串的和。
一開始沒看到“本質不同”,上來就想跑馬拉車 + 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;
}