1.值最大子串
比如{5,-3,4,2}的最大子序列就是 {5,-3,4,2},它的和是8,达到最大;而 {5,-6,4,2}的最大子序列是{4,2},它的和是6。
思路:看子串和是否大于0,大于0继续加,小于0就从当前开始。
int maxSubSum(const vector<int> & arr,int &begin,int &end){
int maxSum=0;
int currSum=0;
int newbegin=0;
for(int i=0;i<arr.size();++i){
currSum+=arr[i];
if(currSum>maxSum){
maxSum=currSum;
begin=newbegin;
end=i;
}
if(currSum<0){
currSum=0;
newbegin=i+1;
}
}
return maxSum;
}
2. 最长无重复子串
子串(substring)——在字符串中是连续的
子序列(subsequence)——在字符串中可以不连续,也可以连续
思路:借助一个哈希表保存每个字符出现的位置
A)如果第i个字符之前没有出现过,那么f(i) = f(i-1)+1;
B)如果第i个字符之前出现过,记第i个字符和它上出现在字符串中的位置的距离,记为d。如果d>f(i-1),那么仍然有f(i) = f(i-1)+1;如果d<=f(i-1),则说明第i个字符上次出现在f(i-1)对应的不重复字符串之内,那么这时候更新 f(i) = d
class Solution {
public:
int longestSubstringWithoutDuplication(const string& str) {
int curLength = 0;
int maxLength = 0;
int* position = new int[26];
for (int i = 0; i < 26; ++i)
position[i] = -1;
for (int i = 0; i < str.length(); ++i) {
int prevIndex = position[str[i] - 'a'];
if (prevIndex < 0 || i - prevIndex > curLength)
++curLength;
else {
if (curLength > maxLength)
maxLength = curLength;
curLength = i - prevIndex;
}
position[str[i] - 'a'] = i;
}
if (curLength > maxLength)
maxLength = curLength;
delete[] position;
return maxLength;
}
};
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int m[256]={0},maxlen=0,left=0;
for(int i=0;i<s.length();++i)
{
if(m[s[i]]==0 || m[s[i]]<left) //从未出现过和未在当前滑动窗口内
maxlen = max(maxlen,i-left+1);
else
{
left = m[s[i]];
}
m[s[i]] = i+1;
}
return maxlen;
}
};
3.最长无重复子序列
思路:没用哈希表,只需要看之前是否出现过。也可以用个哈希表更快。
int lengthOfLongestSubstring(string s) {
int max_len = 0, flag;
for(int i = 0; i < s.length(); i++){
flag = 1;
for(int j = 0; j < i; j++){
if(s[i] == s[j]){
flag = 0;
break;
}
}
if(flag){
max_len++;
}
}
return max_len;
}
4.最长公共子序列
子串(substring)——在字符串中是连续的
子序列(subsequence)——在字符串中可以不连续,也可以连续
示例:比如cnblogs和belong两个字符串,bo, bg, lg,blo,blog都在两个 字符串中出现过, 并且出现顺序一致,求最长公共子序列 为blog。最长公共子串为lo。
思路:
暴力解法
假设 m<n, 对于母串X,我们可以暴力找出2的m次方个子序列,然后依次在母串Y中匹配,算法的时间复杂度会达到指数级O(n∗2的m次)。显然,暴力求解不太适用于此类问题。
动态规划
假设Z=<z1,z2,⋯,zk>是X与Y的LCS, 我们观察到
如果Xm=Yn,则Zk=Xm=Yn,有Zk−1是Xm−1与Yn−1的LCS;
如果Xm≠Yn,则Zk是Xm与Yn−1的LCS,或者是Xm−1与Yn的LCS。
因此,求解LCS的问题则变成递归求解的两个子问题。但是,上述的递归求解的办法中,重复的子问题多,效率低下。改进的办法——用空间换时间,用数组保存中间状态,方便后面的计算。这就是动态规划(DP)的核心思想了。
DP求解LCS
用二维数组c[i][j]记录串x1x2⋯xi与y1y2⋯yj的LCS长度,则可得到状态转移方程
int lcs(String str1, String str2) {
int len1 = str1.length();
int len2 = str2.length();
int **c = new int*[len1+1];
for(int i=0;i<len1+1;++i)
c[i]=new int[len2+1];
for (int i = 0; i <= len1; i++) {
for( int j = 0; j <= len2; j++) {
if(i == 0 || j == 0) {
c[i][j] = 0;
} else if (str1[i-1] == str2[j-1]) {
c[i][j] = c[i-1][j-1] + 1;
} else {
c[i][j] = max(c[i - 1][j], c[i][j - 1]);
}
}
}
return c[len1][len2];
}
5.最长公共子串
思路:同样可以用DP来解决。定义数组的存储含义对于后面推导转移方程显得尤为重要,糟糕的数组定义会导致异常繁杂的转移方程。考虑到子串的连续性,将二维数组c[i][j]用来记录具有这样特点的子串——结尾同时也为为串x1x2⋯xi与y1y2⋯yj的结尾——的长度。
得到转移方程:
最长公共子串的长度为 max(c[i,j]), i∈{1,⋯,m},j∈{1,⋯,n}。
int longsubstr(String str1, String str2) {
int len1 = str1.length();
int len2 = str2.length();
int result = 0; //记录最长公共子串长度
int **c = new int*[len1+1];
for(int i=0;i<len1+1;++i)
c[i]=new int[len2+1];
for (int i = 0; i <= len1; i++) {
for( int j = 0; j <= len2; j++) {
if(i == 0 || j == 0) {
c[i][j] = 0;
} else if (str1[i-1] == str2[j-1]) {
c[i][j] = c[i-1][j-1] + 1;
result = max(c[i][j], result);
} else {
c[i][j] = 0;
}
}
}
return result;
}
参考博客:
https://www.cnblogs.com/zhaogl/p/6364654.html
https://blog.csdn.net/u012102306/article/details/53184446
https://blog.csdn.net/wangdd_199326/article/details/76464333