题意:
In this problem we consider strings over a fixed finite alphabet of size k. The alphabet contains the first k characters from the list
a,b,c,…,z,A,B,C,…,Z,0,1,…,9.
For every test case, we are given the value of k(notice that it cannot exceed 62), and consider only strings consisting of the first k characters from the list.
Given a string s[1…n], we are interested in strings which are not its subsequences. Formally, a string t[1…m] is a subsequence of a string s[1…n] when one can choose not necessarily contiguous‾not necessarily contiguous indices 1≤i1<i2<…im≤n such that t[1]=s[i1],t[2]=s[i2],…,t[m]=t[im]
For example, acb is a subsequence of babcaabbabcaab. Now, given a string s[1…n], we would like to compute the smallest mm such that there is a string t[1…m], which is not a subsequence of s[1…n]. Additionally, we would like to count the number of such shortest strings t[1…m].
As the latter number can be quite large, output it modulo 10^9+7.
Input
The input starts with the number of test cases T≤100.
Then the descriptions of TT test cases follow. A single test case consists of a single line containing the size of the alphabet k(k∈[1,62]) and the string s【1…n】(n∈[1,10^6])]. The string consists of the first k characters from a−zA−Z0−9.
Output
For every test case output one line containing two numbers.
The first number is the smallest mm such that there is a string t[1…m] consisting of the first kkcharacters from a−zA−Z0−9, which is not a subsequence of s[1…n].
The second number is the total count of such shortest strings t[1…m] modulo 10^9+7.
样例输入
3
2 abba
62 0123456789
3 aabbcbbcbabcbab
样例输出
3 5
1 52
4 7
翻译成汉语大概意思就是给你T组样例,每组样例有一个n,代表有n种字符,然后再给出一个字符串。问字符串中不存在的子串的最小长度是多少?有多少种(答案取余1e9+7)?例如 abba,对于字符a,b来说,它不存在的最小子串长度为3,分别为aaa,bbb,aab,baa,bbb
思路:
一道特别难的dp题,关键是思路吧~
我们首先考虑字符串中不存在的子串的最小长度,我们从前往后遍历,每当出现一个没有标记过的字符就标记一次,当n个字符都标记过时,此时子串长度为1的都有了,因此最小长度只能是2,此时我们需要清空标记,继续往后遍历,每当出现一个没有标记过的字符就标记一次,当n个字符都标记过时,此时子串长度为2的都有了(因为每种字符前面一定每种字符至少存在一次),因此最小长度只能是3
我们再来考虑不存在的最小长度子串有多少种,若最小长度子串为1,那么种类为n-字符串中字符的种类数。若最小长度子串大于1时呢?这时候就需要DP推导式了…
给大家举个例子吧:对于3 abbcbabc来说:
从前往后遍历,第一个字符为a,没有标记过,然后标记该字符,此时字符计数num = 1。那么长度为2的以a结尾的子串没有出现的种类数为3(分别是aa,ba,ca),总计数sum[2] = 3。并设置字符a上一次出现的位置为1
第二个字符b,没有标记过,然后标记该字符,此时字符计数num = 2。那么长度为2的以b结尾没有出现的子串种类数为2(分别是bb,cb),总计数sum[2] = 3 + 2 = 5。并设置字符b上一次出现的位置为2
第三个字符b,标记过,此时字符计数num = 2。长度为2的以b结尾没有出现的子串种类数为1(cb),总计数sum[2] = 3 + 2 + 1 - 2 = 4(也就是sum[2] + 1 - 上次b出现加的2)。并设置字符b上一次出现的位置为3
第四个字符c,没有标记过,然后标记该字符,此时字符计数num = 3。那么长度为2的以c结尾没有出现的子串种类数为1(分别是cc),总计数sum[2] = 3 + 2 + 1 - 2 + 1 = 5。并设置字符c上一次出现的位置为4,此时num==字符种类数,因此没出现的最小子串长度至少为2,清空标记与字符计数num
第五个字符b,没有标记过,然后标记该字符,此时字符计数num = 1。那么长度为3的以b结尾没有出现的子串种类数为5(分别是aab,bab,cab,cbb,ccb),总计数sum[3] = sum[2] = 5,由于上一次出现过b且上次出现b是求长度为2的,因此sum[2] = sum[2] - 1 = 4(也就是sum[2]- 上一次b出现增加的1),并设置字符b上一次出现的位置为5
第六个字符a,没有标记过,然后标记该字符,此时字符计数num = 2。那么长度为3的以a结尾没有出现的子串种类数为4(分别是aaa,baa,caa,cca),总计数sum[3] = 5 + 4 = 9,由于上一次出现过a且上次出现a是求长度为2的,因此sum[2] = sum[2] - 3 = 1(也就是sum[2]- 上一次a出现增加的3),并设置字符a上一次出现的位置为6
第七个字符b,标记过,此时字符计数num = 2。那么长度为3的以b结尾没有出现的子串种类数为1(ccb),总计数sum[3] = 5 + 4 + 1 = 10,由于上一次出现过b且上次出现b是求长度为3的,因此sum[3] = sum[3] - 5 = 5(也就是sum[3]- 上一次b出现增加的5),并设置字符b上一次出现的位置为7
第八个字符c,没有标记过,然后标记该字符,此时字符计数num = 3。那么长度为3的以c结尾没有出现的子串种类数为1(ccc),总计数sum[3] = 5 + 1 = 6,由于上一次出现过c且上次出现c是求长度为2的,因此sum[2] = sum[2] - 1 = 0(也就是sum[2]- 上一次c出现增加的1),并设置字符c上一次出现的位置为8,由于num == 字符种类数,因此没出现的最小子串长度至少为3
因此最小子串长度为3,没出现的种类数为sum[3] = 6
原理就是这个原理~只需要线性就能求解
代码:
#include <stdio.h>
#include <string.h>
#define N 1000000
#define mod 1000000007
#define ll long long
#define mem(a) memset(a, 0, sizeof(a))
char s[N + 5]; //存储字符串
int map[300]; //存储字符映射
bool book[70]; //标记数组
ll add[70]; //存储该字符上一次增加的值
int flag[70]; //记录该字符上一次是为了长度为几的子串贡献的add
ll dp[N + 5]; //dp[i]存储长度为i的子串没有出现的种类数
void init() {
int tot = 0;
for (int i = 'a'; i <= 'z'; i++) {
map[i] = tot++;
}
for (int i = 'A'; i <= 'Z'; i++) {
map[i] = tot++;
}
for (int i = '0'; i <= '9'; i++) {
map[i] = tot++;
}
return;
}
int main() {
init();
int T, n;
scanf("%d", &T);
while (T--) {
scanf("%d%s", &n, s);
int len = strlen(s);
mem(book);
mem(dp);
mem(flag);
mem(add);
dp[1] = n;
int chang = 1, num = 0;
for (int i = 0; i < len; i++) {
int t = map[s[i]];
if(chang == 1) {
dp[2] = (dp[2] + n - num + mod) % mod;
if (book[t]) {
dp[2] = (dp[2] - add[t] + mod) % mod;
} else {
dp[1]--;
}
add[t] = n - num;
} else {
dp[chang + 1] = (dp[chang + 1] + dp[chang]) % mod;
ll zj = dp[chang];
if(book[t]) {
dp[chang + 1] = (dp[chang + 1] - add[t] + mod) % mod;
} else {
if (flag[t] == chang - 1) {
dp[chang] = (dp[chang] - add[t] + mod) % mod;
}
}
add[t] = zj;
}
flag[t] = chang;
if(!book[t]) {
num++;
book[t] = 1;
}
if (num == n) {
chang++;
mem(book);
num = 0;
}
}
printf("%d %lld\n", chang, dp[chang]);
}
return 0;
}
如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢