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

迴文自動機(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:當前最長字尾迴文串的節點編號

具體過程看程式碼,程式碼中有註釋

#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;
}