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\)的方案数。
那么有两种转移。
- 对于位数小于\(j\)的数, 显然符合题目要求。
\(\;\)于是\(f_{i, j} += \sum_{k=1}^{j-1} f_{i-j, k}\)
可以用前缀和做到\(O(1)\)转移。 - 对于位数等于\(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;
}