题目:
最长平衡串
给定只含0、1的字符串,找出最长平衡子串的长度(平衡串:包含0和1的个数相同)
输入包含两行:
第一行输入串长n(1=<n<=100000)
第二行输入字符串
Example:
Input:
8
11011011
Output:
4
[思路分析]:
看到题目想到了动态规划中前缀和的思想。定义dp[i]表示前i项和,但是发现,0和1的前i项和并没找到什么规律。
将字符串s中的0看成-1,这样的话,前i项若相等(dp[j]=dp[k],其中k>j),那么,对应下标之间的字符串中0和1的个数相等(即s[j],...s[k-1]中0与1个数相等),只需找到dp[i]相等时,坐标之差的最大值。
得到状态转移方程:
dp[0]=0;
i>=1时,
s[i-1]=0, dp[i]=dp[i-1]-1
s[i-1]=1,dp[i]=dp[i-1]+1
接下来,如何寻找前缀和相等时的下标呢?如何找到最大值?起初想到放到数组里面,记录每个dp[i]的位置,dp[i]相等时,计算位置之差,但是,字符串最长10万,数组开辟空间太大(dp[i]取值-10万到10万),而且,基本上用不到,最好是遍历一遍,若某个dp[i]有记录最起始位置begin,则算长度i-begin,没有的话,就记作起始位置begin=i;
再用一个变量max_substr进行最大值的记录。可以用map,进行dp[i]与其最起始位置的映射。
有关map的简单使用,可参考:https://blog.csdn.net/lady_killer9/article/details/79283548
手算模拟:
下标i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串s | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | |
转换串c | 1 | 1 | -1 | 1 | 1 | -1 | 1 | 1 | |
前缀和dp[i] | 0 | 1 | 2 | 1 | 2 | 3 | 2 | 3 | 4 |
最起始位置begin | 0 | 1 | 2 | 1 | 2 | 5 | 2 | 5 | 8 |
其中,转换串没有必要在代码中体现,判断一下s[i]是0还是1即可,此处是为了读者看着方便。dp[i]要往后多算一个,才能在算最长平衡子串时,包括最后一个。可以看出,dp[i]=0与dp[i]=4时,没有对应平衡串。dp[i]=1时,最长平衡串为10;dp[i]=2时,最长平衡串为0110;dp[i]=3时,最长平衡串为01。综上,最长平衡串长度为4,即0110。
代码:
/*
Project:
Date: 2019/01/07
Author: Frank Yu
题目:最长平衡串
给定只含0、1的字符串,找出最长平衡子串的长度(平衡串:包含0和1的个数相同)
输入包含两行:
第一行输入串长n(1=<n<=100000)
第二行输入字符串
Example:
Input:
8
11011011
Output:
4
Project:
Date: 2019/01/07
Author: Frank Yu
[思路分析]:看到题目想到了动态规划中前缀和的思想。定义dp[i]表示前i项和,但是发现,0和1的前i项和并没找到什么规律。
将字符串s中的0看成-1,这样的话前i项若相等(dp[j]=dp[k],其中k>j),那么,对应下标之间的字符串中0和1的
个数相等(即s[j],...s[k-1]中0与1个数相等)
得到状态转移方程:
dp[0]=0;
i>=1时,
s[i-1]=0, dp[i]=dp[i-1]-1
s[i-1]=1,dp[i]=dp[i-1]+1
接下来,如何寻找前缀和相等时的下标呢?如何找到最大值?起初想到放到数组里面,记录每个dp[i]的位置,
dp[i]相等时,计算位置之差,但是,字符串最长10万,数组开辟空间太大(dp[i]取值-10万到10万),而且,
基本上用不到,最好是遍历一遍,若某个dp[i]有记录最起始位置begin,则算长度i-begin,没有的话,就记作起始位置begin=i;
再用一个变量max_substr进行最大值的记录。可以用map,进行dp[i]与其最起始位置的映射。
*/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<set>
#include<list>
#include<vector>
#include<map>
#include<iterator>
#include<algorithm>
#include<iostream>
#define Maxsize 100000
using namespace std;
int main()
{
int len=0;int dp[Maxsize];
int start = 0;
int begin = 0;
int max_substr = 0;
map<int, int> mymap;//映射dp[i]与其对应的最起始位置
string s="";
memset(dp,0,sizeof(dp));
cin >> len >> s;
dp[0] = 0;
//cout << "dp[0]=" << dp[0] << endl;
for (int i=1;i<=len;i++)//注意等于,dp[i]的下标多一个,因为要算前缀和,最后一个也要算
{
if('0'==s[i-1]) dp[i] = dp[i - 1] - 1;
else dp[i] = dp[i - 1] + 1;
//cout << "dp[" << i << "]=" << dp[i]<<endl;
}
//利用map记录每个dp[i]的最起始位置
for (int i=1;i<=len;i++)
{
//得到dp[i]对应的起点
begin = mymap[dp[i]];
//cout << "begin:" << begin << endl;
if (begin == 0 && dp[i] != 0) { //除去第一个已记录的dp[i]与begin
mymap[dp[i]] = i; //记录dp[i]最起始位置
//cout << mymap[dp[i]] <<endl;
}
else {
//更新当前dp[i]最大子串
if (i - begin > max_substr) {
max_substr = i - begin;
start = begin;
//cout << "start:"<< start<<endl;
}
}
}
cout<< max_substr<<endl;
/*输出最长子串(若长度相等,输出起始下标最小的)
cout << s.substr(start,max_substr);*/
return 0;
}
结果截图:

代码中有调试时的一些输出,读者可以复制代码,输出看一下。
更多数据结构与算法实现:数据结构(严蔚敏版)与算法的实现(含全部代码)
有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。