题目链接
- 题意:长度为 n n n 的字符串中有多少个长度为 k k k 并且不同的子序列。
- 定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示截至 s [ i − 1 ] s[i-1] s[i−1] 为止,长度为 j j j 的子序列的个数
-
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
+
d
p
[
i
−
1
]
[
j
−
1
]
dp[i][j]=dp[i-1][j]+dp[i-1][j-1]
dp[i][j]=dp[i−1][j]+dp[i−1][j−1],啥意思呢?
d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]表示截至 s [ i − 2 ] s[i-2] s[i−2] 为止,长度为 j j j 的子序列的个数
d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i−1][j−1]则表示选上 s [ i − 1 ] s[i-1] s[i−1] 之后长度为 j j j 的子序列的个数 - 但是会有重复的子序列,如何去重呢?
答:记录每个字符邻近的前一个相同字符出现的位置,那么重复的部分就是 d p [ p r e [ i ] − 1 ] [ j − 1 ] dp[pre[i]-1][j-1] dp[pre[i]−1][j−1]也就是以该字符结尾,长度为 j j j的子序列的个数。
注意:长度为0的子序列数目为1~
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
int read()
{
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = -f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 1003;
const ll mod = 1e9+7;
ll dp[maxN][maxN];
int n, k;
char s[maxN];
int pos[26], pre[maxN];
int main()
{
n = read(), k = read(); cin.getline(s, sizeof(s));
for(int i = 0; i <= n; ++ i ) dp[i][0] = 1;
for(int i = 0; i <= n; ++ i )
{
if(pos[s[i] - 'a'])
pre[i + 1] = pos[s[i] - 'a'];
pos[s[i] - 'a'] = i + 1;
}
for(int i = 1; i <= n; ++ i )
{
for(int j = 1; j <= i; ++ j )
{
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
if(pre[i])
dp[i][j] -= dp[pre[i] - 1][j - 1];
dp[i][j] %= mod;
}
}
printf("%lld\n", (dp[n][k] + mod) % mod);
return 0;
}