一维动态规划
int *dp;
// 自顶向下记忆化搜索,时间复杂度O(n)
int recursive(int n) {
if (n == 0)return 0;
if (n == 1) return 1;
// 若之前计算过就直接返回
if (dp[n] != -1) return dp[n];
dp[n] = recursive(n - 2) + recursive(n - 1);
return dp[n];
}
int fib(int n) {
dp = (int *) malloc(sizeof(int) * (n + 1));
memset(dp, -1, sizeof(int) * (n + 1));
return recursive(n);
}
// 自下而上,时间复杂度O(n)
int fib(int n) {
int dp[31];
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= n; ++i)
// 状态转移方程
dp[i] = dp[i - 2] + dp[i - 1];
return dp[n];
}
// 状态压缩,时间复杂度O(n)
int fib(int n) {
if (n < 2) return n;
int left = 0;
int mid = 1;
int right;
for (int i = 2; i <= n; ++i) {
right = left + mid;
left = mid;
mid = right;
}
return right;
}
// todo 矩阵快速幂,时间复杂度O(logn)
// 代入通项公式
int fib(int n) {
double sqrt5 = sqrt(5);
double fibN = pow((1 + sqrt5) / 2, n) - pow((1 - sqrt5) / 2, n);
// 四舍五入成正数
return round(fibN / sqrt5);
}
// 打表
// 每种方案对应的通行天数
int durations[3] = {1, 7, 30};
int min(int a, int b) {
return a > b ? b : a;
}
// 返回从第day[i]天开始往后的行程全部完成所需的最小花费
int recursive(int *days, int daysSize, int *costs, int curIndex) {
if (curIndex == daysSize) return 0;
int res = 0x7fffffff;
// 一共三种方案
for (int i = 0; i < 3; ++i) {
// day[index]为下一个需要买票的日子
int index = curIndex;
// 可以连续通行的最后一天的后一天
int nextDay = days[index] + durations[i];
while (index < daysSize && days[index] < nextDay)
index++;
// 记录最小值
res = min(res, costs[i] + recursive(days, daysSize, costs, index));
}
return res;
}
// 暴力法超时,时间复杂度O(3^n)
int mincostTickets(int *days, int daysSize, int *costs, int costsSize) {
return recursive(days, daysSize, costs, 0);
}
// 每种方案对应的通行天数
int durations[3] = {1, 7, 30};
int *dp;
int min(int a, int b) {
return a > b ? b : a;
}
// 返回从第day[i]天开始往后的行程全部完成所需的最小花费
int recursive(int *days, int daysSize, int *costs, int curIndex) {
if (curIndex == daysSize) return 0;
if (dp[curIndex] != 0x7fffffff) return dp[curIndex];
int res = 0x7fffffff;
// 一共三种方案
for (int i = 0; i < 3; ++i) {
// day[index]为下一个需要买票的日子
int index = curIndex;
// 可以连续通行的最后一天的后一天
int nextDay = days[index] + durations[i];
while (index < daysSize && days[index] < nextDay)
index++;
// 记录最小值
res = min(res, costs[i] + recursive(days, daysSize, costs, index));
}
dp[curIndex] = res;
return res;
}
// 记忆化搜索,时间复杂度O(3n)=O(n)
int mincostTickets(int *days, int daysSize, int *costs, int costsSize) {
dp = (int *) malloc(sizeof(int) * daysSize);
for (int i = 0; i < daysSize; ++i) dp[i] = 0x7fffffff;
return recursive(days, daysSize, costs, 0);
}
int min(int a, int b) {
return a > b ? b : a;
}
// 自下而上
int mincostTickets(int *days, int daysSize, int *costs, int costsSize) {
// 每种方案对应的通行天数
int durations[3] = {1, 7, 30};
int dp[daysSize + 1];
for (int i = 0; i < daysSize; ++i) dp[i] = 0x7fffffff;
dp[daysSize] = 0;
for (int curIndex = daysSize - 1; curIndex >= 0; curIndex--) {
// 一共三种方案
for (int i = 0; i < 3; ++i) {
// day[index]为下一个需要买票的日子
int index = curIndex;
// 可以连续通行的最后一天的后一天
int nextDay = days[index] + durations[i];
while (index < daysSize && days[index] < nextDay)
index++;
// 记录最小值
dp[curIndex] = min(dp[curIndex], dp[index] + costs[i]);
}
}
return dp[0];
}
int len;
// 返回从curIndex位置往后的字符串有多少种解码方式
int recursive(char *s, int curIndex) {
// 返回1表示到末尾结束了,之前的解码算是一种方案
if (curIndex == len) return 1;
int res;
// 当前位置是0,无法解码
if (s[curIndex] == '0') {
res = 0;
} else {
// i位置可以对应一个字符
res = recursive(s, curIndex + 1);
int val = (s[curIndex] - '0') * 10 + (s[curIndex + 1] - '0');
if (curIndex + 1 < len && val <= 26)
// i和i+1位置合在一起也能构成一个字母
res += recursive(s, curIndex + 2);
}
return res;
}
// 暴力法超时,O(2^n)
int numDecodings(char *s) {
len = strlen(s);
return recursive(s, 0);
}
int len;
int *dp;
// 返回从curIndex位置往后的字符串有多少种解码方式
int recursive(char *s, int curIndex) {
// 返回1表示到末尾结束了,之前的解码算是一种方案
if (curIndex == len) return 1;
if (dp[curIndex] != -1) return dp[curIndex];
int res;
// 当前位置是0,无法解码
if (s[curIndex] == '0') {
res = 0;
} else {
// curIndex位置可以对应一个字符
res = recursive(s, curIndex + 1);
if (curIndex + 1 < len && (s[curIndex] - '0') * 10 + (s[curIndex + 1] - '0') <= 26)
// curIndex和curIndex+1位置合在一起也能构成一个字母
res += recursive(s, curIndex + 2);
}
dp[curIndex] = res;
return res;
}
// 记忆化搜索,O(n)
int numDecodings(char *s) {
len = strlen(s);
dp = (int *) malloc(sizeof(int) * len);
memset(dp, -1, sizeof(int) * len);
return recursive(s, 0);
}
int numDecodings(char *s) {
int len = strlen(s);;
int *dp = (int *) malloc(sizeof(int) * (len + 1));
dp[len] = 1;
for (int curIndex = len - 1; curIndex >= 0; curIndex--) {
if (s[curIndex] == '0') {
// 当前位置是0,无法解码
dp[curIndex] = 0;
} else {
// curIndex位置可以对应一个字符
dp[curIndex] = dp[curIndex + 1];
if (curIndex + 1 < len && (s[curIndex] - '0') * 10 + (s[curIndex + 1] - '0') <= 26)
// curIndex和curIndex+1位置合在一起也能构成一个字母
dp[curIndex] += dp[curIndex + 2];
}
}
return dp[0];
}
// 状态压缩
// 类似有条件的斐波那契数列
int numDecodings(char *s) {
int len = strlen(s);;
int left, mid = 1, right;
for (int curIndex = len - 1; curIndex >= 0; curIndex--) {
if (s[curIndex] == '0') {
// 当前位置是0,无法解码
left = 0;
} else {
// curIndex位置可以对应一个字符
left = mid;
if (curIndex + 1 < len && (s[curIndex] - '0') * 10 + (s[curIndex + 1] - '0') <= 26)
// curIndex和curIndex+1位置合在一起也能构成一个字母
left += right;
}
right = mid;
mid = left;
}
return left;
}
int len;
const int mod = 1e9 + 7;
// 返回从curIndex位置往后有多少种有效转化
int recursive(char *s, int curIndex) {
if (curIndex == len) return 1;
// 1 转不了
if (s[curIndex] == '0') return 0;
// 2 curIndex单独转换。当前位置是*,则有9种转法;是正常的非0数,则有一种转法
int res = (s[curIndex] == '*' ? 9 : 1) * recursive(s, curIndex + 1);
// 3 curIndex和curIndex+1一起转
if (curIndex + 1 < len) {
// 存在curIndex+1的位置
// 根据是否是*,分为4种情况
if (s[curIndex] != '*') {
if (s[curIndex + 1] != '*') {
// 3.1 num num
// 能转成1~26,才算一种
if ((s[curIndex] - '0') * 10 + s[curIndex + 1] - '0' <= 26)
res += recursive(s, curIndex + 2);
} else {
// 3.2 num *
if (s[curIndex] == '1')
// 11~19,9种转发
res += 9 * recursive(s, curIndex + 2);
if (s[curIndex] == '2')
// 21~26,6种转发
res += 6 * recursive(s, curIndex + 2);
}
} else {
if (s[curIndex + 1] != '*') {
// 3.3 * num
if (s[curIndex + 1] <= '6')
// 在num<=6时,*可以是1或2,1num、2num,2种
res += 2 * recursive(s, curIndex + 2);
else
// 在num>6时,*只能为1,1种
res += recursive(s, curIndex + 2);
} else {
// 3.4 * *
// 11~19 21~26 15种
res += 15 * recursive(s, curIndex + 2);
}
}
}
return res % mod;
}
// 暴力法超时
int numDecodings(char *s) {
len = strlen(s);
return recursive(s, 0);
}
int len;
const int mod = 1e9 + 7;
int *dp;
// 返回从curIndex位置往后有多少种有效转化
long long recursive(char *s, int curIndex) {
if (curIndex == len) return 1;
// 1 转不了
if (s[curIndex] == '0') return 0;
// 从备忘录种返回
if (dp[curIndex] != -1) return dp[curIndex];
// 2 curIndex单独转换。当前位置是*,则有9种转法;是正常的非0数,则有一种转法
long long res = (s[curIndex] == '*' ? 9 : 1) * recursive(s, curIndex + 1);
// 3 curIndex和curIndex+1一起转
if (curIndex + 1 < len) {
// 存在curIndex+1的位置
// 根据是否是*,分为4种情况
if (s[curIndex] != '*') {
if (s[curIndex + 1] != '*') {
// 3.1 num num
// 能转成1~26,才算一种
if ((s[curIndex] - '0') * 10 + s[curIndex + 1] - '0' <= 26)
res += recursive(s, curIndex + 2);
} else {
// 3.2 num *
if (s[curIndex] == '1')
// 11~19,9种转发
res += 9 * recursive(s, curIndex + 2);
if (s[curIndex] == '2')
// 21~26,6种转发
res += 6 * recursive(s, curIndex + 2);
}
} else {
if (s[curIndex + 1] != '*') {
// 3.3 * num
if (s[curIndex + 1] <= '6')
// 在num<=6时,*可以是1或2,1num、2num,2种
res += 2 * recursive(s, curIndex + 2);
else
// 在num>6时,*只能为1,1种
res += recursive(s, curIndex + 2);
} else {
// 3.4 * *
// 11~19 21~26 15种
res += 15 * recursive(s, curIndex + 2);
}
}
}
dp[curIndex] = res % mod;
return dp[curIndex];
}
// 记忆化搜索
int numDecodings(char *s) {
len = strlen(s);
dp = (int *) malloc(sizeof(int) * len);
memset(dp, -1, sizeof(int) * len);
return recursive(s, 0);
}
// 自下而上,严格位置依赖
int numDecodings(char *s) {
const int mod = 1e9 + 7;
int len = strlen(s);
// dp[curIndex]返回从curIndex位置往后有多少种有效转化
long long *dp = (long long *) malloc(sizeof(long long) * (len + 1));
dp[len] = 1;
for (int curIndex = len - 1; curIndex >= 0; curIndex--) {
// 1 转不了
if (s[curIndex] == '0') {
dp[curIndex] = 0;
continue;
}
// 2 curIndex单独转换。当前位置是*,则有9种转法;是正常的非0数,则有一种转法
dp[curIndex] = (s[curIndex] == '*' ? 9 : 1) * dp[curIndex + 1];
// 3 curIndex和curIndex+1一起转
if (curIndex + 1 < len) {
// 存在curIndex+1的位置
// 根据是否是*,分为4种情况
if (s[curIndex] != '*') {
if (s[curIndex + 1] != '*') {
// 3.1 num num
// 能转成1~26,才算一种
if ((s[curIndex] - '0') * 10 + s[curIndex + 1] - '0' <= 26)
dp[curIndex] += dp[curIndex + 2];
} else {
// 3.2 num *
if (s[curIndex] == '1')
// 11~19,9种转发
dp[curIndex] += 9 * dp[curIndex + 2];
if (s[curIndex] == '2')
// 21~26,6种转发
dp[curIndex] += 6 * dp[curIndex + 2];
}
} else {
if (s[curIndex + 1] != '*') {
// 3.3 * num
if (s[curIndex + 1] <= '6')
// 在num<=6时,*可以是1或2,1num、2num,2种
dp[curIndex] += 2 * dp[curIndex + 2];
else
// 在num>6时,*只能为1,1种
dp[curIndex] += dp[curIndex + 2];
} else {
// 3.4 * *
// 11~19 21~26 15种
dp[curIndex] += 15 * dp[curIndex + 2];
}
}
}
dp[curIndex] %= mod;
}
return (int) dp[0];
}
// 自下而上,严格位置依赖+状态压缩
int numDecodings(char *s) {
const int mod = 1e9 + 7;
int len = strlen(s);
// left返回从curIndex位置往后有多少种有效转化
long long left, mid = 1, right;
for (int curIndex = len - 1; curIndex >= 0; curIndex--) {
// 1 转不了
if (s[curIndex] == '0') {
left = 0;
right = mid;
mid = left;
continue;
}
// 2 curIndex单独转换。当前位置是*,则有9种转法;是正常的非0数,则有一种转法
left = (s[curIndex] == '*' ? 9 : 1) * mid;
// 3 curIndex和curIndex+1一起转
if (curIndex + 1 < len) {
// 存在curIndex+1的位置
// 根据是否是*,分为4种情况
if (s[curIndex] != '*') {
if (s[curIndex + 1] != '*') {
// 3.1 num num
// 能转成1~26,才算一种
if ((s[curIndex] - '0') * 10 + s[curIndex + 1] - '0' <= 26)
left += right;
} else {
// 3.2 num *
if (s[curIndex] == '1')
// 11~19,9种转发
left += 9 * right;
if (s[curIndex] == '2')
// 21~26,6种转发
left += 6 * right;
}
} else {
if (s[curIndex + 1] != '*') {
// 3.3 * num
if (s[curIndex + 1] <= '6')
// 在num<=6时,*可以是1或2,1num、2num,2种
left += 2 * right;
else
// 在num>6时,*只能为1,1种
left += right;
} else {
// 3.4 * *
// 11~19 21~26 15种
left += 15 * right;
}
}
}
left %= mod;
right = mid;
mid = left;
}
return (int) left;
}
bool isUgly(int n) {
if (n <= 0) return false;
// 如果n能被2整除,就除掉一个2
while (n % 2 == 0) n /= 2;
while (n % 3 == 0) n /= 3;
while (n % 5 == 0) n /= 5;
return n == 1;
}
int min(int a, int b) {
return a > b ? b : a;
}
int min3(int a, int b, int c) {
return min(min(a, b), c);
}
int nthUglyNumber(int n) {
// dp[i]为第i个丑数
int dp[n + 1];
dp[1] = 1;
int i2 = 1, i3 = 1, i5 = 1;
int curIndex = 2;
while (curIndex <= n) {
int val2 = dp[i2] * 2;
int val3 = dp[i3] * 3;
int val5 = dp[i5] * 5;
int m = min3(val2, val3, val5);
// 没有else,因为可能有多个指针同时往后走
if (m == val2) i2++;
if (m == val3) i3++;
if (m == val5) i5++;
dp[curIndex++] = m;
}
return dp[n];
}
int longestValidParentheses(char *s) {
int len = strlen(s);
if (len <= 1) return 0;
// dp[i]表示以s[i]结尾的最长有效括号的长度
int dp[len];
for (int i = 0; i < len; ++i) dp[i] = 0;
int max = 0;
for (int i = 1; i < len; ++i) {
// 以'('结尾,无法形成有效括号,以')'结尾才有可能
if (s[i] == ')') {
// 以s[i-1]为结尾的最长有效括号的开头的左边一个字符的下标
int index = i - 1 - dp[i - 1];
if (index >= 0 && s[index] == '(')
dp[i] = dp[i - 1] + 2 + (index > 0 ? dp[index - 1] : 0);
}
// 记录最大
if (dp[i] > max) max = dp[i];
}
return max;
}
int findSubstringInWraproundString(char *s) {
int len = strlen(s);
int str[len];
// 转成对应的0~26
for (int i = 0; i < len; ++i)
str[i] = s[i] - 'a';
// dp[i]表示以i+'a'结尾的字符向左,按照base串规则的最大延伸长度
// 也代表了以i+'a'结尾,符合条件的可能总数
int dp[26] = {0};
dp[str[0]] = 1;
int pre, cur, count = 1;
for (int i = 1; i < len; ++i) {
cur = str[i];
pre = str[i - 1];
if ((pre + 1) % 26 == cur)
count++;
else
count = 1;
if (count > dp[cur])
dp[cur] = count;
}
int res = 0;
for (int i = 0; i < 26; ++i)
res += dp[i];
return res;
}
// todo
int distinctSubseqII(char *s) {
int len = strlen(s);
int mod = 1e9 + 7;
// 总数(包括了空集)
int all = 1;
// 新增的个数
int newAdd;
// count[s[i] - 'a']表示以s[i]结尾的子序列个数
int count[26] = {0};
for (int i = 0; i < len; ++i) {
newAdd = (all - count[s[i] - 'a'] + mod) % mod;
all = (all + newAdd) % mod;
count[s[i] - 'a'] = (count[s[i] - 'a'] + newAdd) % mod;
}
return (all - 1 + mod) % mod;
}