洛谷P2697 宝石串
洛谷P2697 宝石串
题目大意
给一个只有 \(\texttt{R}\) 和 \(\texttt{G}\) 的字符串,求一个 \(\texttt{R}\) 和 \(\texttt{G}\) 数量相同的区间,输出这个区间的最大长度。
题解
1. 暴力
把所有 \(\texttt{R}\) 标记为 \(1\),把所有 \(\texttt{G}\) 标记为 \(-1\),求一下前缀和数组 \(s\)。
二重循环枚举左端点和右端点 \(i\),\(j\),如果 \(s_{i-1}=s_j\)(即 \(s_j-s_{i-1}=0\)),则说明当前区间为稳定区间。
如果当前区间为稳定区间,更新答案,最后输出最大答案即可。
时间复杂度为 \(\mathcal O (n^2)\)。
2. 优化方案
由暴力的启发,我们可以用一个数组 \(m\) 记录每个前缀和第一次出现的位置,之后再遇到这个前缀和就会产生一个稳定区间。
由于前缀和可能为负数,所以把 \(m\) 数组下标加上该字符串的长度。
时间复杂度减小到 \(\mathcal O (n)\)。
代码
\(\mathcal O (n^2)\) 暴力:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
char t[1000005];
int a[1000005], s[1000005], ans;
int main()
{
cin >> t;
int n = strlen(t);
for (int i = 1; i <= n; i++)
{
if (t[i - 1] == 'R')
{
a[i] = 1;
}
else if (t[i - 1] == 'G')
{
a[i] = -1;
}
}
for (int i = 1; i <= n; i++)
{
s[i] = a[i] + s[i - 1];
if (s[i] == 0)
{
ans = max(ans, i);
}
}
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j += 2)
{
if (s[j] == s[i - 1])
{
ans = max(ans, j - i + 1);
}
}
}
cout << ans;
return 0;
}
\(\mathcal O (n)\) 优化方案:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int s[1000005], a[1000005], m[1000005], ans;
char t[1000005];
int main()
{
cin >> t;
int n = strlen(t);
for (int i = 0; i < n; i++)
{
if (t[i] == 'R')
{
a[i + 1] = 1;
}
else
{
a[i + 1] = -1;
}
}
for (int i = 1; i <= n; i++)
{
s[i] = s[i - 1] + a[i]; //预处理前缀和
}
for (int i = 1; i <= 2 * n + 2; i++)
{
m[i] = -1;
}
m[n + 1] = 0;
for (int i = 1; i <= n; i++)
{
if (m[s[i] + n + 1] >= 0)
{
ans = max(ans, i - m[s[i] + n + 1]); //更新答案
}
else
{
m[s[i] + n + 1] = i; //记录该前缀和第一次出现的位置
}
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号