回文自动机(2018ACM-ICPC南京赛区网络赛: I. Skr)

 

I. Skr

A number is skr, if and only if it's unchanged after being reversed. For example, "12321", "11" and "1" are skr numbers, but "123", "221" are not. FYW has a string of numbers, each substring can present a number, he wants to know the sum of distinct skr number in the string. FYW are not good at math, so he asks you for help.

Input

The only line contains the string of numbers SS.

It is guaranteed that 1 \le S[i] \le 91≤S[i]≤9, the length of SS is less than 2000000

Output

Print the answer modulo 1000000007

样例输入1

111111

样例输出1

123456

 

题意:

给你一个长度为n的数字串,只包含数字1~9,求出所有本质不同的回文串代表的整数之和

例如:"1232111":ans = 1+11+111+2+3+232+12321 = 12681

 

回文自动机:回文字符串问题通解

本质:维护一棵回文树,回文树有以下性质:

  1. 初始有两个根(节点编号0和1),0的所有子孙都是长度为偶数的回文串,1的所有子孙都是长度为奇数的回文串
  2. 除了两个根以外,每个节点都代表一个本质不同的回文串
  3. 和AC自动机相似

每个节点维护:

  1. len[i]:编号为i的节点表示的回文串长度,其中len[0]=0,len[1]=-1
  2. ch[i][c]:在编号为i的节点表示的回文串两边添加字符c以后变成的新回文串的编号(这个节点的儿子)
  3. fail[i]:节点i表示的回文串的最长后缀回文串的编号(例如32323→323→3)
  4. cnt[i]:节点i表示的回文串在整个串中出现了多少次

一些变量如下:

  • cnt:节点编号(从0开始到cnt-1)
  • last:当前最长后缀回文串的节点编号

具体过程看代码,代码中有注释

参考博客:https://blog.csdn.net/Clove_unique/article/details/53750322

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long
#define mod 1000000007
char str[2000005];
int cnt, cur, last, fail[2000005], sum[2000005], len[2000005], ch[2000005][12];
LL num[2000005], p[2000005] = {1};
int Add(int x)
{
	len[cnt++] = x;
	return cnt-1;
}
int Getfail(int x, int id)
{
	while(str[id-len[x]-1]!=str[id])
		x = fail[x];
	return x;
}
int main(void)
{
	LL ans;
	int n, now, i;
	scanf("%s", str+1);
	str[0] = '#', fail[0] = 1;
	Add(0), Add(-1), last = 0;
	n = strlen(str+1);
	for(i=1;i<=n;i++)
	{
		num[i] = (num[i-1]*10+str[i]-'0')%mod;
		p[i] = p[i-1]*10%mod;
	}
	ans = 0;
	for(i=1;i<=n;i++)
	{
		cur = Getfail(last, i);		//找到以当前字符为结尾的最长回文串所在编号
		if(ch[cur][str[i]-'0']==0)		//如果找不到,说明出现了一个新的本质不同的回文串,新建节点
		{
			now = Add(len[cur]+2);
			ans = (ans+num[i]-num[i-len[now]]*p[len[now]]%mod+mod)%mod;		//计算答案
			fail[now] = ch[Getfail(fail[cur], i)][str[i]-'0'];			//和AC自动机一样建立fail指针
			ch[cur][str[i]-'0'] = now;
		}
		last = ch[cur][str[i]-'0'];
		sum[last]++;
	}
	for(i=cnt-1;i>=0;i--)
		sum[fail[i]] += sum[i];
	printf("%lld\n", ans);
	
	
	/*for(i=2;i<=cnt-1;i++)		//生成各个节点的数值,可以拿来理解程序
		printf("%d ", len[i]);
	puts("");
	for(i=2;i<=cnt-1;i++)
		printf("%d ", fail[i]);
	puts("");
	for(i=2;i<=cnt-1;i++)
		printf("%d ", sum[i]);
	puts("");*/	
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值