题目:给定一个字符串,字符串中只包含‘0’和‘1’。请找到一个最长的子串,使得其中0和1的数量是相同的
例1:“10101010” 结果就是其本身
例2:“0011110”结果是“0011”
扩展:给定一个字符串,字符串只包含‘(’和‘)’,请找出一个最长的子串,使得该子串满足括号匹配法则。
例1:“(()())()()”结果为自身
例2:“())()()(”结果为“()()"
错误的解法:(没考虑仔细)
解:扩展后题目要求更严格一点,但解题思路是一样的:
用栈保存字符,扫描字符串,如果
1)当前字符与栈顶匹配(‘0’和‘1’相互匹配,‘)’与‘(’匹配),删除栈顶,计算长度,遍历下一个字符。
2)当前字符与栈顶不匹配,当前字符入栈,遍历下一个字符。
代码1:与讲解稍有不同,栈内存的是下标不是字符,这样方便计算长度。
时间复杂度:O(n),空间复杂度:最坏为O(n)
#include
#include
#include
using namespace std;
void longestSubsequence(const string &str, int &s, int &t){
stack q;
int maxLen = 0;
for(int i = 0; i
PS:此代码与”柱状图中找最大矩形 & 矩阵中找最大的仅含相同值的矩形区域”的代码有点相似。
此解法总是尽量向左匹配,错误!例子:0011110000100 结果不正确。
============================割=========================================
正确的解法:
解法I:直观的动态规划解法:
f[i] = f[j] + (i - j), 其中j为max{ j | j < i,并且 串"i+1, i+2,...,j"为合法串(0、1个数相等,或者 满足括弧法则)}
若找不到满足条件的j,则f[i] = 0;
最后的结果为 max{ f[i] }。
此算法时间复杂度为O(n^2),空间复杂度O(n),同时适用原问题和扩展问题。
解法II:对解法I进行优化:
由于解法I需要遍历找j,所以时间复杂度比较高。
实际上,可以根据01串的特点,在O(1)的时间确定出j。
扫描字符串,将扫描到的0的个数与1的个数的差,记录在A数组中,则A[i]值在区间[-n, n],n为总的字符个数。
状态转移方程可改写为:
f[i] = f[j] + (i - j), 其中j为max{ j | j < i,并且 A[j] == A[i] }。
如果在算f的时候,同时记录数组B,B[k]表示差值为k的最后出现的位置,注意此处k理论上可以为负值,那么状态转移方程可以改写为:
f[i] = f[j] + (i - j), 其中j=B[A[i]]。
此算法,时间复杂度O(n),空间复杂度3 * O(n)。 仅适用于原问题。
解法III:对解法II进行 简化:
仔细观察会发现,f[i]的值等于:差值A[i]最早出现的位置,与i的距离。所以可以对解法II进行简化,见代码2
此算法,时间复杂度与解法II一样,同为O(n),空间复杂度同为O(n),但系数变小了2 * O(n)。 仅适用于原问题。
代码2:
#include
#include
#include
#include
using namespace std;
void longestSubsequence(const string &str, int &s, int &t){
int strLen = str.size();
vector v(strLen * 2 + 1, -1);
int maxLen = 0;
int diff = 0;
for(int i = 0; i