AtCoder Beginner Contest 381 Solution
A - 11/22 String
题意
给定一个长度为N的字符串S,判断其是不是11/22串。
11/22串的定义是在/两边有等长的1和2。
解法
按照题意判断一下即可。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
string s;
cin >> n >> s;
if (n % 2 == 1 && s.substr(0, n / 2) == string(n / 2, '1') && string(n / 2, '2') == s.substr(n / 2 + 1) && s[n / 2] == '/') {
cout << "Yes" << endl;
}
else {
cout << "No" << endl;
}
}
B - 1122 String
题意
给定一个字符串S,判断他是不是1122串。
1122串的定义是字符串里字母形如aabbcc成对出现,并且每一对字符互不相同。
解法
记录一下字母是否出现过即可。
#include<bits/stdc++.h>
using namespace std;
string s;
bool vis[10005];
int main()
{
cin >> s;
if (s.size() % 2 == 1) {
cout << "No" << endl;
return;
}
for (int i = 0;i < s.size();i += 2) {
if (vis[s[i]]) {
cout << "No" << endl;
return;
}
else {
if (s[i] != s[i + 1]) {
cout << "No" << endl;
return;
}
vis[s[i]] = 1;
}
}
cout << "Yes" << endl;
}
C - 11/22 Substring
题意
给定长度为N的字符串S,找到里面最长的11/22串的长度是多少。
解法
11/22串的显著特征是在正中间有个/,并且11/22串相互之间是不可能交融的,那么我们枚举找/的位置,然后向左右扩展到最大的11/22串,然后让i往后跳就行了。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, ans = 0;
string s;
cin >> n >> s;
for (int i = 0;i < n;i++)
{
if (s[i] == '/') {
int k=1;
while(i-k>=0 && i+k<n && s[i-k]=='1' && s[i+k]=='2') k++;
k--;
ans = max(ans, k*2+1);
i+=k;
}
}
cout << ans << endl;
}
D - 1122 Substring
题意
给定N个数,问里面最长的1122串是多长。
解法
不难想到一个1122串要么是在奇数位置上与前一个相同,要么是在偶数位置上与前一个相同,其实情况是一样的,为方便思考,我们分开考虑。
考虑递推,枚举i,记录tmp作为包含当前位置的合法1122串的最远起点。
分类讨论:
a[i]!=a[i-1],当前与上一个数不相同,那么不可能有包含当前位置的合法1122串,所以令tmp=i+1;
a[i]==a[i-1],新的a[i]和a[i-1]这一对数相同,如果想被加入之前的1122串,那么要求就是不能与前面1122串里面的数字相同。所以记录last[j]表示上一次出现j的位置,那么可得tmp=max(tmp,last[a[i]]+1);
然后更新一下ans=max(ans,i-tmp+1)即可。
做完了,奇数起点和偶数起点各做一遍就可以了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200005;
int n;
int a[maxn];
int las[maxn];
void clea() {
for (int i = 1;i <= n;i++) {
las[i] = -2;
}
}
int main()
{
cin >> n;
for (int i = 0;i < n;i++) {
cin >> a[i];
}
clea();
int ans = 0, tmp = 0;
for (int i = 1;i < n;i += 2) {
if (a[i] != a[i - 1]) {
tmp = i + 1;
continue;
}
tmp = max(tmp, las[a[i]] + 1);
ans = max(ans, i - tmp + 1);
las[a[i]] = i;
}
clea();
tmp = 1;
for (int i = 2;i < n;i += 2) {
if (a[i] != a[i - 1]) {
tmp = i + 1;
continue;
}
tmp = max(tmp, las[a[i]] + 1);
ans = max(ans, i - tmp + 1);
las[a[i]] = i;
}
cout << ans << endl;
return 0;
}
E - 11/22 Subsequence
题意
给定长度为N的字符串S和Q个询问,每次询问LR,问L到R的区间内最长的可不连续11/22串长度是多少。
解法
依然关键点在于/上,不难发现对于任何一个区间内的/,以他为中心的最长11/22串取决于min(左1数,右2数),然而他们左边的1的数量是递增的,2的数量是递减的。
举例: 1 2 / 1 2 / 1 2 / 1 2 / 1 2 1
左边1的数量:1 2 3 4
右边2的数量:4 3 2 1
想要使得min(左1数,右2数)尽量大,显然越靠近中间的越大。
我们设左1数为c1,右2数为c2,那么我们可以分析得到,对于这个序列,可以分为两段:前段是c1<c2,后段是c1>c2,并且分界线上的答案是最大的。
那么解法已经呼之欲出了,找两段的分界线毫无疑问是二分答案!我们二分/的位置,就可以找到分界线的最大值了。至于怎么求c1和c2,通过前缀和和后缀和就可以轻松得到。
#include <bits/stdc++.h>
using namespace std;
int n, q, t[3][100005];
vector<int> g;
string s;
int main()
{
cin >> n >> q;
cin >> s;
s = " " + s;
for (int i = 1; i <= n; i++)
{
if (s[i] == '/')
g.push_back(i);//记录每一个/的位置
else
t[s[i] - '0'][i]++;
}
for (int i = 1;i <= n;i++)
t[1][i] += t[1][i - 1];//对1做前缀和
for (int i = n;i >= 1;i--)
t[2][i] += t[2][i + 1];//对2做后缀和(其实前缀和也可以,方便理解)
while (q--)
{
int L, R, ans = -1;
cin >> L >> R;
int l = lower_bound(g.begin(), g.end(), L) - g.begin(), r = upper_bound(g.begin(), g.end(), R) - g.begin() - 1;//l和r为g数组中的斜杠的区间
while (l <= r)
{
int mid = (l + r) >> 1;
int c1 = t[1][g[mid]] - t[1][L - 1];//求出g[mid]前到L有几个1
int c2 = t[2][g[mid]] - t[2][R + 1];//求出g[mid]后到R有几个2
ans = max(ans, min(c1, c2));
if (c1 > c2)
r = mid - 1;
else
l = mid + 1;
}
if (ans != -1)
ans = ans * 2 + 1;
else
ans = 0;
cout << ans << endl;
}
return 0;
}