传送门
定义 f [ i ] [ j ] f[i][j] f[i][j]是以 [ 1 , i ] [1,i] [1,i]形成长度为 j j j的子序列数量
普通的转移方程为
f [ i ] [ l e n ] = f [ i − 1 ] [ l e n − 1 ] + f [ i − 1 ] [ l e n ] f[i][len]=f[i-1][len-1]+f[i-1][len] f[i][len]=f[i−1][len−1]+f[i−1][len]
但是这样显然是多算了
考虑 j < i j<i j<i且 a [ j ] = = a [ i ] a[j]==a[i] a[j]==a[i]
那么 [ 1 , j − 1 ] [1,j-1] [1,j−1]和 j j j形成的子序列, [ 1 , j − 1 ] [1,j-1] [1,j−1]和 i i i也能形成
所以显然是需要减去 f [ p r e [ a i − ′ a ′ ] − 1 ] [ l e n − 1 ] f[pre[a_i-'a']-1][len-1] f[pre[ai−′a′]−1][len−1]
所以这样去 d p dp dp即可
代码
不过我的做法稍稍有点不一样
我直接从每个字母出现的最后一个位置转移过来
这样每个字母的状态都是包括了前面所有的答案
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5+10;
const int mod = 1e9+7;
int n,k,vis[maxn],ans,f[1009][1009];
char a[maxn];
signed main()
{
cin >> n >> k >> (a+1);
if( k==0 ) { cout << 1; return 0; }
for(int i=1;i<=n;i++)
{
f[i][1] = 1;
for(int j=2;j<=k;j++)//枚举每一个长度
{
for(int pre=0;pre<=25;pre++)
f[i][j] = ( f[i][j]+f[vis[pre]][j-1] )%mod;
}
vis[a[i]-'a'] = i;
}
int ans = 0;
for(int i=0;i<=25;i++) ans = ( ans+f[vis[i]][k] )%mod;
cout << ans;
}