题目总结:二分求[x-n,x+n]区间 和 求字串
两个考点:
- 如何求某子串在字符串中的位置集合
- 如何高效判断两个集合中的所有数的,绝对值之差小于某一个数
求子串的位置:
由于我们要求Alice和Bob的位置,而字符串中两个合法的单词是:在两个标点符号之间的串
所以求出所有的合法单词,再判断是不是Alice或者Bob
string temp = "";
for (int i = 0; i < n; i++)
{
// 如果s[i]是符号 那么s[i-1]之前的就是一个单词
// 或许会有第一个字符就是标点符号,但没有关系,if中又判断了内容
if (!isalpha(s[i]))
{
if (temp == "Alice")
va.push_back(i - temp.size() + 1);
else if (temp == "Bob")
vb.push_back(i - temp.size() + 1);
// 注意每次清空
temp = "";
}
else // 说明当前单词还没扫描完 继续完善单词
temp += s[i];
}
TLE的朴素算法 $O(N^2)$:
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
if (va[i] < vb[j] && (vb[j] - (va[i] + 4) - 1 <= K))
ans++;
if ((va[i] > vb[j]) && (va[i] - (vb[j] + 2) - 1 <= K))
ans++;
}
高效算法 二分$O(NlogN)$:
遍历全部的Alice的位置,若Alice当前位置为x
则二分寻找Bob的位置集合中,符合条件的区间
分别二分左端点和右端点
有边界问题:
二分左的时候:如果Bob的位置没有比x小的,那么左端点的结果应该是比x大的第一个 那么这样也不矛盾
二分右的时候:如果Bob的位置没有比x大的,那么右端点的结果应该是比x小的第一个 这样也是右端点 不矛盾
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int main()
{
int K;
string s;
cin >> K;
cin.get(); // 接收空字符
getline(cin, s);
vector<int> va, vb;
s += '.';
// 由于下面的切分是分别符号来的 所以最后可能不是符号结尾,即:******Alice 这种情况
int n = s.size();
// 单个单词
string temp = "";
for (int i = 0; i < n; i++)
{
// 如果s[i]是符号 那么s[i-1]之前的就是一个单词
if (!isalpha(s[i]))
{
if (temp == "Alice")
va.push_back(i - temp.size() + 1);
else if (temp == "Bob")
vb.push_back(i - temp.size() + 1);
// 注意每次清空
temp = "";
}
else
temp += s[i];
}
// 二分求区间
n = va.size();
int m = vb.size();
long long ans = 0;
for (int i = 0; i < n; i++)
{
int l = 0, r = m - 1;
while (l < r)
{
int mid = l + r >> 1;
if (va[i] - (vb[mid] + 2) - 1 <= K)
r = mid;
else
l = mid + 1;
}
int ansl = l;
// 注意 再acwing上不加这个是可以过的
// y总应该少考虑一个情况:有合法的Alice和Bob可是没有“同时出现”的
// 意思是看找到的区间的左边界是否符合 如果不符合就是没有找到左边界
if (va[i] - (vb[ansl] + 2) - 1 > K)
continue;
l = 0, r = m - 1;
while (l < r)
{
// cout << l << " * " << r << endl;
int mid = l + r + 1 >> 1;
if (vb[mid] - (va[i] + 4) - 1 <= K)
l = mid;
else
r = mid - 1;
}
// 同上
if (vb[l] - (va[i] + 4) - 1 > K)
continue;
ans += (r - ansl + 1);
// cout << ansl << ' ' << r << endl;
}
cout << ans;
cout << endl;
// 调试使用
// for(auto e: va)
// cout << e << " ";
// cout << endl;
// for(auto e: vb)
// cout << e<< " ";
return 0;
}
很详细