数据范围
输入数字位数 [1,101]。
样例
输入:“12258”
输出:5
思路:使用dp[i]表示子字符串[0,i]的翻译方法数,则dp[i]=dp[i-1]+dpi-2
代码:
class Solution {
public:
const static int maxn = 105;
int dp[maxn];
int getTranslationCount(string s) {
dp[1] = 1;
dp[0] = 1;
int n = s.length();
for(int i = 2; i <= n; i++) {
dp[i] = dp[i - 1];
if(s[i-2] != '0' && stoi(s.substr(i-2,2)) < 26) {
dp[i] += dp[i-2];
}
}
return dp[n];
}
};
acwing30. 正则表达式匹配
请实现一个函数用来匹配包括’.‘和’*'的正则表达式。
模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(含0次)。
在本题中,匹配是指字符串的所有字符匹配整个模式。
例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配。
数据范围
输入字符串长度 [0,300]
。
样例
输入:
s=“aa”
p=“a*”
输出:true
思路:用dp[i][j]表示i开头的带匹配串和j开头的模式串是否匹配,遍历模式串,如果模式串当前字符是字母或者’.',则dp[i][j]=dp[i-1][j-1]+(字符是否匹配),如果当前字符是*
,则可分两种情况,一种是不使用*
前面的字符dp[i][j]=dp[i][j-2]+(字符是否匹配),一种是使用*前面的字符,dp[i][j] |= dp[i-1][j-2]
代码:
class Solution {
public:
const static int maxn = 305;
int dp[maxn][maxn];
bool isMatch(string s, string p) {
dp[0][0] = 1;
int n = s.length(), m = p.length();
for(int i = 0; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(p[j - 1] == '*') {
dp[i][j] |= dp[i][j-2];
if(i >= 1 && j > 1 && (s[i - 1] == p[j - 2] || p[j - 2] == '.'))
{
dp[i][j] |= dp[i-1][j];
}
}
else if(i > 0){
dp[i][j] = dp[i-1][j-1] && (s[i-1] == p[j-1] || p[j-1] == '.');
}
}
}
return dp[n][m];
}
};
分段式递推
acwing 80. 骰子的点数
将一个骰子投掷 n次,获得的总点数为 s,s的可能范围为 n∼6n。
掷出某一点数,可能有多种掷法,例如投掷 2次,掷出 3点,共有 [1,2],[2,1]两种掷法。
请求出投掷 n次,掷出 n∼6n点分别有多少种掷法。
数据范围
1≤n≤10
样例1
输入:n=1
输出:[1, 1, 1, 1, 1, 1]
解释:投掷1次,可能出现的点数为1-6,共计6种。每种点数都只有1种掷法。所以输出[1, 1, 1, 1, 1, 1]。
样例2
输入:n=2
输出:[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]
解释:投掷2次,可能出现的点数为2-12,共计11种。每种点数可能掷法数目分别为1,2,3,4,5,6,5,4,3,2,1。
所以输出[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]。
思路:令dp[i][k]表示通过k次摇骰子获得数字i的情况数,则dp[i][k] = sum(dp[i-j][k-1]), sum表示求和,j~[1,6]
代码:
class Solution {
public:
const static int maxn = 100;
int dp[maxn][maxn];
vector<int> numberOfDice(int n) {
int m = n * 6;
vector <int> ans;
dp[0][0] = 1;
for(int i = 1; i <= m; i++) {
for(int k = 1; k <= n; k++) {
for(int j = 1; j <= 6; j++) {
if(i - j >= 0) {
dp[i][k] += dp[i - j][k - 1];
}
}
}
}
for(int i = n; i <= m; i++) {
ans.push_back(dp[i][n]);
}
return ans;
}
};
分割序列:
题面:定义f(A)表示将序列A进行unique操作之后的序列的元素个数。unique操作是指将相邻目相同的元素合成一个元素,再按照原序列的相对顺序进行排列之后得到的序列。例如[1 1 1 2 2 3 3 1]进行unique操作之后的序列为[1 2 3 1] ;[1 2 3 3 2 1 1]进行unique操作之后的序列为[1,2,3,2,1];[1111]进行unique 操作之后得到的序列为1。现在,输入一个长度为n的序列S,你需要将其划分为k段,使得每一段都不为空,且你需要最大化所有段的f函数的值之和。你只需要输出这个最大值就行
样例输入:
8 3
1 1 1 2 2 3 3 1
样例输出:
6
思路:(没找到正解,打暴力版递推式)dp[i][j]=max(dp[l][j-1] + f(i:j), dp[l][j-1])
代码:(暂时没想到正解,目前复杂度O(nnk*log(m), m为数组中不同元素的个数)
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5 + 5;
const int N = 1e3 + 5;
int a[maxn];
int dp[maxn][N];
map <int, int> mp;
int main() {
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
dp[1][1] = 1;
for(int i = 1; i <= n; i++) {
for(int l = 1; l <= k; l++) {
mp.clear();
int cnt = 0;
for(int j = i; j >= 1; j--) {
if(!mp.count(a[j]))cnt++;
mp[a[j]]=1;
dp[i][l] = max(dp[i][l], dp[j][l - 1] + cnt);
}
}
}
cout << dp[n][k] << endl;
return 0;
}