传送门
思路:
使用一个数组来记录到当前index的最大子序列的长度是多少。
上面这个图就是当5大于2的时候,我们就需要更新辅助数组的值。
int lengthOfLIS(vector& nums) {
int size = nums.size();
vector helper(size, 1);
int res = 0;
for (int i = 0; i < size; ++i) {
for (int j = 0; j < i; ++j) {
helper[i] = (nums[i] > nums[j] && helper[i] < helper[j] + 1) ? helper[j] + 1 : helper[i];
}
res = res > helper[i] ? res : helper[i];
}
return res;
}
从代码可以看出 j 要小于 i ,然后找出 i 之前的子序列的最大值,helper[i]就可以得到到当前index的最长子序列的长度。
传送门
思路:
因为是子串,是连续的,那么还是用begin和end两个变量来保存回文的开头和结尾,然后求得长度,再计算起始的子串index即可。
public:
string longestPalindrome(string s) {
int size = s.size();
if (size == 0 || size == 1) return s;
int mBegin = 0, len = 0;
for (int i = 0; i < size-1; ++i) {
int one = getMax(s, size, i, i);
int two = getMax(s, size, i, i+1);
int curMax = max(one, two);
if (len < curMax) {
len = curMax;
mBegin = i - ((len - 1) >> 1);
}
}
return string(s, mBegin, len);
}
private:
int getMax(string& s, int size, int begin, int end) {
while (begin >= 0 && end < size && s[begin] == s[end]) {
begin--;
end++;
}
return end - begin - 1;
}
值得注意的是:one,two这两个长度,是为了计算单数长度的子串和双数子串的长度。
传送门
思路:
使用一个二维数组来帮助我们记录长度
d[i][j]代表 s[i:j]的最长的回文子序列的长度。
状态转移方程为: d[i][j] = if s[i] == s[j] : d[i+1][j-1] + 2 else max(d[i+1][j], d[i][j-1]);
int longestPalindromeSubseq(string s) {
int size = s.size();
int* helper[size];
for (int i = 0; i < size; ++i) {
helper[i] = new int[size];
memset(helper[i], 0, sizeof(int) * size);
}
int res = 0;
for (int i = 0; i < size; ++i) {
helper[i][i] = 1;
res = res > helper[i][i] ? res : helper[i][i];
for (int j = i-1; j >= 0; --j) {
if (s[i] == s[j]) helper[i][j] = helper[i-1][j+1] + 2;
else helper[i][j] = max(helper[i-1][j], helper[i][j+1]);
res = res > helper[i][j] ? res : helper[i][j];
}
}
return res;
}
传送门
思路:
也是使用一个二维数组来帮助我们,d[i][j]代表s[i:j]子串的回文长度,如果s[i:j]不是回文,那么d[i][j]为0。
状态转移方程为:d[i][j] = if s[i] == s[j] && d[i+1]d[j-1] != 0 : d[i+1][j-1] + 2 else 0;
int countSubstrings(string s) {
int size = s.size();
int* helper[size];
for (int i = 0; i < size; ++i) {
helper[i] = new int[size];
memset(helper[i], 0, sizeof(int) * size);
}
int res = size;
for (int i = 0; i < size; ++i) {
helper[i][i] = 1;
for (int j = i-1; j >= 0; --j) {
if (j+1 == i && s[j] == s[i]) {
helper[i][j] = 2;
res++;
} else if (s[j] == s[i] && helper[i-1][j+1] != 0) {
helper[i][j] = helper[i-1][j+1] + 2;
res++;
} else {
helper[i][j] = 0;
}
}
}
return res;
}
特别要注意循环里面的第一个判断:j+1 == i 这个也是为了处理当两个相同的字符连在一起的情况。
传送门
思路:
使用一个M*N的二维数组来帮助我们,跟编辑距离差不多。
d[i][j] 代表的是 s1[0:i] 和 s2[0:j]两个子串的公共后缀长度。
d[i][j] = if s1[i] == s2[j] : d[i-1][j-1] + 1 else 0;
int LongestCommonSubString(string s1, string s2) {
int size1 = s1.size(), size2 = s2.size();
int* helper[size1];
for (int i = 0; i < size1; ++i) {
helper[i] = new int[size2];
memset(helper[i], 0, sizeof(int) * size2);
}
int res = 0;
for (int i = 0; i < size1; ++i) {
for (int j = 0; j < size2; ++j) {
if (s1[i] == s2[j] && i == 0 || j == 0) helper[i][j] = 1;
else if (s1[i] == s2[j]) helper[i][j] = helper[i-1][j-1] + 1;
else helper[i][j] = 0;
res = res > helper[i][j] ? res : helper[i][j];
}
}
return res;
}
传送门
思路:
跟上面子串差不多,也是一个M*N的二维数组,但是当s[i] != s[j] 的时候,需要记录的是 d[i][j-1] 和 d[i-1][j]的最大值,为的是记录之前的最长子序列,不会跟上面那样断掉。
d[i][j] = if s1[i] == s2[j] : d[i-1][j-1] + 1 else max(d[i-1][j], d[i][j-1]);
int LongestCommonSubString(string s1, string s2) {
int size1 = s1.size(), size2 = s2.size();
int* helper[size1];
for (int i = 0; i < size1; ++i) {
helper[i] = new int[size2];
memset(helper[i], 0, sizeof(int) * size2);
}
int res = 0;
for (int i = 0; i < size1; ++i) {
for (int j = 0; j < size2; ++j) {
if (s1[i] == s2[j] && i == 0 || j == 0) helper[i][j] = 1;
else if (s1[i] == s2[j]) helper[i][j] = helper[i-1][j-1] + 1;
else helper[i][j] = max(helper[i-1][j], helper[i][j-1]);
res = res > helper[i][j] ? res : helper[i][j];
}
}
return res;
}
传送门
思路:
如果目标是10,有1,2,5三种硬币,那么我们需要从1开始算,需要一个一维数组来帮助我们保存目标是index的时候最少需要多少枚硬币。
int coinChange(vector& coins, int amount) {
int size = coins.size();
vector helper(amount+1, 0);
for (int i = 1; i <= amount; ++i) {
int curMin = INT_MAX;
for (int j = 0; j < size; ++j) {
int left = i - coins[j];
if (left > 0 && helper[left] > 0) {
curMin = curMin > helper[left] + 1 ? helper[left] + 1 : curMin;
} else if (left == 0) {
curMin = 1;
break;
}
}
helper[i] = curMin == INT_MAX ? -1 : curMin;
}
return helper[amount];
}
可以看到上面你的循环是从1开始的,代表我们需要从面值为1的时候开始慢慢往后算。注意当left == 0时:这个时候是代表正好有这个面值的硬币,那么最小值当然是1。直接break内层循环,如果没有找到当前这个面值合适的组合(即没有解)那么填入特殊值-1。