题目链接:点击此处
题目大意:一个长度为n的数组,问其删除了m个字符后,所有不同的子串有多少个
首先我们来看看这个题目的简化版:
51 Nod 1202 子序列个数
题目很裸,就是问一个数组子序列的个数
我们用dp来解决这个问题,dp[i]表示前i个字符能够组成多少个不同的子序列
首先每次对于一个数的加入
如果这个数没有在前面出现过
如果我们选择这个数,显然这个数可以和前面所有的子序列组成一个新的子序列
所以dp[i]=2*dp[i-1]
如果这个数在前面出现过,首先我们找到距离其最近出现的位置pos
对于选择了这个数的情况
比如:1 2 3 4 5 6 7 5
这个时候5不能和前面所有子序列组成一个新的子序列
对于pos位置之前所有的不同的子序列,和这个数字组成的所有子序列都已经通过pos位置那个相同的数算了一遍
所以我们这里要减去重复的数
dp[i]=2*dp[i]-dp[pos]
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long llt;
const int MOD=1e9+7;
const int MAXN=111111;
int arr[MAXN];
int last[MAXN];
llt dp[MAXN];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
memset(last,0,sizeof(dp));
for(int i=1;i<=n;i++)
scanf("%d",&arr[i]);
dp[0]=1;
for(int i=1;i<=n;i++){
int val=arr[i];
if(last[val]){
dp[i]=(2*dp[i-1]-dp[last[val]-1]+MOD)%MOD;
}else dp[i]=2*dp[i-1]%MOD;
last[val]=i;
}
printf("%lld\n",dp[n]-1);
}
return 0;
}
然后我们回到正题,对于这个题目来说,解题思路和上面那个题目差不多,但是这个题目多了一个长度的限制
我们用dp[i][j]表示前i个数字,删除了j个字符,最多能组成多少个不同的子序列
情况和前面类似,如果这个数字没有在前面出现过,那么对于这个数字我们可以选择删或者不删
dp[i][j]=dp[i-1][j-1]+dp[i-1]][j]
如果这个数字在前面出现过,那么我们找到距离其最近的位置
首先因为dp[i][j]表示在前i个字符串删除了j个字符,也就是说取了(i-j)个数
如果我们这里选择了这个数,也就是说在前i-1个位置里面取了(i-1-j)个数
这里和之重复的部分是选择了这个字符出现的上一个位置,并且在前(last[val]-1)个位置选择了(i-1-j)个数
也就是说在前(last[val]-1)个数中删除了(last[val]-1)-(i-1-j)个数
所以这里dp[i][j]=dp[i-1][j-1]+dp[i-1]][j]+dp[last[val]-1][(last[val]-1)-(i-1-j)]
附上代码~~
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
//题目问的是长度为k的字符串删除了k个子串后
//存在多少个互不相同的子序列
typedef long long llt;
const int MOD=1e9+7;
const int SIZE=1e5+111;
int arr[SIZE];
llt dp[SIZE][11];
int last[15];
int main()
{
int n,m,z;
while(scanf("%d%d%d",&n,&m,&z)!=EOF)
{
memset(last,0,sizeof(last));
for(int i=1;i<=n;i++)
scanf("%d",&arr[i]);
//无论长度如何,如果一个数字都没删除的子序列有且仅有一种
for(int i=0;i<=n;i++) dp[i][0]=1;
for(int i=1;i<=n;i++){
int val=arr[i];
for(int j=1;j<=m;j++){
if(!last[val]) dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%MOD;
else{
//因为前面出现了这个数,所以我们要去掉重复的
//首先这个地方长度应该已经取了
int pos=(i-j-1);
int dl=last[val]-pos-1;
if(dl>=0) dp[i][j]=(dp[i-1][j]+dp[i-1][j-1]-dp[last[val]-1][dl]+MOD)%MOD;
else dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%MOD;
}
}
last[val]=i;
}
printf("%lld\n",dp[n][m]);
}
return 0;
}