sequence

题目

Description
给定一个长度为\(n\)的由['0'..'9']组成的字符串\(s\)\(v[i,j]\)表示由字符串\(s\)\(i\)到第\(j\)位组成的十进制数字。
将它的某一个上升序列定义为: 将这个字符串切割成m段不含前导'0'的串, 切点分别为\(k_1, k_2\dots k_{m-1}\), 使得\(v[1,k_1]<v[k_1 + 1, k2]<\dots<v[k_{m-2}+1,k_{m-1}]\)
请你求出该字符串\(s\)的上升序列个数, 答案对 \(10^9+7\) 取模。

Input
第一行一个整数\(n\), 表示字符串长度;
第二行\(n\)['0'..'9']内的字符, 表示给出的字符串s。

Output
仅一行表示给出字符串s的上升序列个数对\(10^9+7\)取模的值。

Sample Input 1
6
123434

Sample Output 1
8

Sample Input 2
8
20152016

Sample Output 2

4

HINT
\(\;\)对于\(30%\)的数据满足: \(n\leq10\)
\(\;\)对于\(100%\)的数据满足: \(n\leq5000\)

题解

\(f_{i, j}\)表示前\(i\)个数, 最后一段长度为\(j\)的方案数。
那么有两种转移。

  1. 对于位数小于\(j\)的数, 显然符合题目要求。
    \(\;\)于是\(f_{i, j} += \sum_{k=1}^{j-1} f_{i-j, k}\)
    可以用前缀和做到\(O(1)\)转移。
  2. 对于位数等于\(j\)的数, 如满足\(v[i-2j+1, i-j] < v[i-j+1, i]\), 那么\(f_{i, j} += f{i-j, j}\)

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;


const int mod = 1e9 + 7;


char str[5010];


int lcp[5010][5010];

int n;


bool cmp(int x, int y)
{
	int l = min(y - x - 1, lcp[x][y]);
	return str[x + l] < str[y + l];
}


int f[5010][5010], sum[5010][5010];


int main()
{
	scanf("%d", &n);
	scanf("%s", str + 1);
	
	for (int i = n; i >= 1; i--)
		for (int j = i + 1; j <= n; j++)
			if (str[i] == str[j])
				lcp[i][j] = lcp[i+1][j+1] + 1;
	
	sum[0][0] = 1;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			sum[i][j] = sum[i][j-1];
			if (str[i-j+1] == '0') continue;
			f[i][j] = (f[i][j] + sum[i-j][min(j-1, i-j)]) % mod;
			if (2 * j <= i)
				if (cmp(i-2*j+1, i-j+1))
					f[i][j] = (f[i][j] + f[i-j][j]) % mod;
			sum[i][j] = (sum[i][j] + f[i][j]) % mod;
		}
	}
	
	printf("%d\n", sum[n][n]);
	
	return 0;
}
posted @ 2019-08-22 20:40  EZ_WYC  阅读(201)  评论(0编辑  收藏  举报