ACM-ICPC 2018南京赛区网络赛 I.Skr (回文树)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Cymbals/article/details/82707686

题目:https://nanti.jisuanke.com/t/30998

计蒜客开了禁止复制,我挂了破解网页限制的脚本能复制但是不太好看,就不贴题目了。

题目大意是给出一个数字串,求其本质不同的回文子串的和。

一开始没看到“本质不同”,上来就想跑马拉车 + 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][9];
	int fail[maxn];
	int len[maxn];
	// 记录数字
	ll num[maxn];
	ll ans;
	int S[maxn];
	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;
}

猜你喜欢

转载自blog.csdn.net/Cymbals/article/details/82707686