dynamic programming and linear programming, techniques of very broad applicability
that can be invoked when more specialized methods fail.
6.4 Knapsack
1025 Keep at Most 100 Characters
Given a string which contains only lower case letters, how many different non-empty strings you can get if you can keep AT MOST 100 characters in the original order? (Note: The string obtained is a “sub-sequence” of the original string.)
Input Specification:
Each input file contains one test case, which is only one line containing the string whose length is no larger than 1000.
Output Specification:
Output the number of different non-empty strings if you can only keep at most 100 characters. Since the answer might be super large, you only need to output the answer modulo 1000000007.
从初始想法上看
前i个字符串的所有子序列 和 第i个字符进行组合并去重复之后再想加就是 前i+1个的结果
输入一个字符串
但是我们希望坐标从1开始
方便我们的dp
string s;
cin>>s;
//坐标从0开始 solution1
const int maxn = 1005;
char s[maxn];
cin>>s+1;
int n = strlen(s+1);
//solution2 这样应该也可以
string t;
cin>>t;
string s = '-';
s += t;
dp[i][j] 前i个 子序列长度为j的 数量
生成字符串的所有子序列字符串
子序列长度可以从1到n
我们将字符串前i个(前缀子串)作为一个子问题
对于当前第i个字符 可能是子序列最后一个字符也可能不是 两个状态
答案由这两个状态组成 - 重复值
可以当成背包
字符串的字符是物品, 容量是序列
每个物品可以选择作为最后一个拿 和 不是最后一个拿
dp[i][j]的值就是前i个物品刚好拿j个物品的取法
dp[i][j] = dp[i-1][j] + dp[i-1][j] -repeat;
repeat = [以当前为结尾的字符上一次出现位置(作为最后一次拿)](取)[形成j-1个子序列]
[pos[s[i]] - 1][j-1]
#include <bits/stdc++.h>
using namespace std;
int main(){
char s[1005];
cin>>s+1;
int n = strlen(s+1);
int dp[n+1][n+1];
for(int i = 0;i<=n;i++){
dp[i][0] = 1;
}
for(int i = 1;i<=n;i++){
dp[0][i] = 0;
}
map<char,int> pos;
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
if(j > i){dp[i][j] = 0; break;}
int rep = 0;
if(pos[s[i]] != 0){
rep = dp[pos[s[i]] -1 ][j-1];
}
dp[i][j] = dp[i-1][j]+dp[i-1][j-1] - rep;
}
pos[s[i]] = i;
}
long long ans = 0;
for(int i = 1;i<=n;i++){
ans += dp[n][i];
}
cout<<ans<<endl;
}
取模运算处理
#include <bits/stdc++.h>
using namespace std;
const int mod = 1000000007;
const long long M = 1e9+7;
int main(){
char s[1005];
cin>>s+1;
int n = strlen(s+1);
long dp[n+1][n+1];
for(int i = 0;i<=n;i++){
for(int j= 0;j<=n;j++){
dp[i][j] = 0;
}
}
for(int i = 0;i<=n;i++){
dp[i][0] = 1;
}
for(int i = 1;i<=n;i++){
dp[0][i] = 0;
}
int limit = n <= 100 ? n:100;
map<char,int> pos;
for(int i = 1;i<=n;i++){
for(int j = 1;j<=limit;j++){
if(j > i){dp[i][j] = 0; break;}
int rep = 0;
if(pos[s[i]] != 0){
rep = dp[pos[s[i]] -1 ][j-1]%M;
}
dp[i][j] = dp[i-1][j]%M+dp[i-1][j-1]%M - rep%M;
dp[i][j] %= M;
}
pos[s[i]] = i;
}
long long ans = 0;
for(int i = 1;i<=limit;i++){
ans += dp[n][i];
ans %= M;
}
cout<<ans<<endl;
}