题目大意:有一个长度为n的由0和1组成的字符串,现要求选择两个子串,使得这两个子串的按位或最大,求这个最大值,
5<=n<=1e6
思路:因为要按位或最大,所以我们选择的一个子串一定是原字符串去掉前缀0后的子串,之后要想或运算的结果最大,就要让最靠左的0都优先变成1,所以我们枚举从最靠左的0左边的每个1开头的子串,每个子串的长度为从第一个0到末尾的长度,然后找到所有子串中最大的那个,时间复杂度为O(第一个1到1右边第一个0的长度*这个0到原字符串末尾的长度)因为每个位置上是0或1的概率为1/2,所以1右边的第一个0有很大概率出现前10个位置,时间复杂度近似于O(n)所以按以上方法枚举不会超时,如果数据不是随机生成的,那么时间复杂度肯定是O(n*n)
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e6 + 5;
char s[N];
int pos0;
int main()
{
int n;
cin >> n;
int cnt = 0;
char c;
getchar();
bool flag = 0;//判断有没有1
int pos1;//记录第一个1的位置
for (int i = 0; i < n; i++)
{
scanf("%c", &s[i]);
if (!flag && s[i] == '1')
{
flag = 1;
pos1 = i;
}
if (!cnt&&flag && s[i] == '0')
{
pos0 = i;//记录第一个右边第一个0的位置
cnt++;//统计0的数量
}
}
if (!flag)
{//字符串中没有1,直接输出0
printf("0");
return 0;
}
if (flag&&!cnt)
{//字符串中没有0//直接输出1
for (int i = 1; i <= n; i++)
{
printf("1");
}
return 0;
}
string ans;
for (int i = pos1; i < pos0; i++)
{//枚举以第一个1到第一个0之间的每个1为子串开头
string temp = s;
for (int j = i, k = pos0; k < n; j++, k++)
{//维护两个指针从第一个1开始遇到一个1,就把第一个0右边对应位置变成1
if (s[j] == '1')
temp[k] = '1';
}
ans = max(temp, ans);//找出所有子串中的最大值
}
for (int i = pos1; i < n; i++)
{//从第一个1开始到n输出
printf("%c", ans[i]);
}
return 0;
}