牛客多校H题题解
链接:[https://ac.nowcoder.com/acm/contest/81597/H]
来源:牛客网
题目描述
Red stands at the coordinate
of the Cartesian coordinate system. She has a string of instructions: up, down, left, right (where 'right' increases the x-coordinate by , and 'up' increases the y-coordinate by ).Now Red wants to select a continuous substring of instructions and execute them. Red hopes that the final execution of the instructions can pass through the coordinate . She wants to know how many selection options there are.
输入描述:
The first line contains three integers
, , and ( , )( , ), —the length of the instruction string and the coordinates Red hopes to pass through.
The second line contains a string of length 𝑛n, consisting of the characters '
', ' ', ' ', and ' '. — the four directions: up, down, left, and right, respectively.
输出描述:
Output one integer representing the number of selection options for the continuous substring.
解题大概思路
该题目我们想的暴力思路大概就是通过构建两个前缀和数组来,每次通过哈希表查找前缀和与目标
(或者 )的对应的前缀和是否存在,如果存在,就把该区间存入区间另外一个哈希表内.这一部分的时间复杂度为 级别 然后跑了一遍
循环之后,再遍历一遍存储着符合条件的区间的哈希表,从哈希表里面选出符合要求的区间,这里特别要注意统计区间的时候要考虑清楚,避免重复统计区间
代码处理:
特殊判定 起点为(0,0)的情况下:
if(!x && !y) {
cout << n * (n + 1) / 2 << nn;
return;
}
初始化哈希表,记录出现过的区间
mp[{0, 0}].push_back(0);
set<pair<int, int>> se;
初始化前缀和数组
sx[i] = sx[i - 1] + dx[s[i]];
sy[i] = sy[i - 1] + dy[s[i]];
每次插入一个二维前缀和的时候,通过 用 的时间复杂度来进行查询是否查找到了所需要的区间
像我比赛时写的代码进行了三次哈希查找,其中有一个哈希查找还相互嵌套,时间复杂度大概是
auto v = mp[{sx[i] - x, sy[i] - y}];
//取出 右边的下标
if(!v.empty()) {
//如果找到了对应的一个前缀和
for(auto l: v) {
// if(l == i) se.insert({l, i});
if(l + 1 <= i) se.insert({l + 1, i});
//前面的if判断其实很多余
}
}
最后再在大的set里面找出所需要的区间
其实这里就算不用位图去重也可以的,因为此时哈希表里面的区间都是满足题目意思的区间
-
所以这里我感觉直接输出
se.size()
就是最佳答案
for(auto x: se) {
int l = x.first, r = x.second;
if(vis[l]) continue;
vis[l] = 1;
ans += n - r + 1;
//如果有效就加上 n-r+1
}
AC code:
#include<bits/stdc++.h>
#define int long long
#define nn '\n'
using namespace std;
const int maxn = 2e5 + 5;
int sx[maxn], sy[maxn]; // 位置从1开始
bitset<maxn> vis;
void solve() {
int n, x, y, ans = 0;
string s;
cin >> n >> x >> y >> s;
if(!x && !y) {
cout << n * (n + 1) / 2 << nn;
return;
}
//特殊判定 起点为(0,0)的情况下
s = '0' + s;
map<char, int> dx, dy;
dx['D'] = 1, dx['A'] = -1;
dy['W'] = 1, dy['S'] = -1;
map<pair<int, int>, vector<int>> mp;
//初始化哈希表,记录出现过的区间
mp[{0, 0}].push_back(0);
set<pair<int, int>> se;
for(int i = 1; i <= n; i++) {
sx[i] = sx[i - 1] + dx[s[i]];
sy[i] = sy[i - 1] + dy[s[i]];
//初始化前缀和数组
mp[{sx[i], sy[i]}].push_back(i);
//直接这样插入元素???
auto v = mp[{sx[i] - x, sy[i] - y}];
//取出 右边的下标
if(!v.empty()) {
//如果找到了对应的一个前缀和
for(auto l: v) {
// if(l == i) se.insert({l, i});
if(l + 1 <= i) se.insert({l + 1, i});
//前面的if判断其实很多余
}
}
}
// for(auto x: se) {
// cout << x.first << ' ' << x.second << nn;
// }
set<string> ss;
//然后现在要对存入区间内的有效区间0进行判断
for(auto x: se) {
int l = x.first, r = x.second;
if(vis[l]) continue;
vis[l] = 1;
ans += n - r + 1;
//如果有效就加上 n-r+1
}
cout << ans << nn;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _ = 1;
// cin >> _;
while(_--)
solve();
return 0;
}
这里附上一篇大佬的代码,简洁优雅简直让人拜服!
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, a, b, ans;
char c;
pair<int, int> m[1000005];
map<pair<int, int>, int> t;
signed main()
{
cin >> n >> a >> b;
if (a == 0 && b == 0) {
cout << n * (n + 1) / 2;
return 0;
}
for (int i = 1; i <= n; i++) {
cin >> c;
if (c == 'A') m[i] = {m[i - 1].first - 1, m[i - 1].second + 0};
if (c == 'S') m[i] = {m[i - 1].first + 0, m[i - 1].second - 1};
if (c == 'W') m[i] = {m[i - 1].first + 0, m[i - 1].second + 1};
if (c == 'D') m[i] = {m[i - 1].first + 1, m[i - 1].second + 0};
}
for (int i = n; i > 0; i--) {
t[{m[i].first - a, m[i].second - b}] = n - i + 1;
ans += t[{m[i - 1].first, m[i - 1].second}];
}
cout <<br ans;
return 0;
}
那么这段AC代码到底包含着什么样的逻辑呢??
1.首先通过设置前缀和来统计各个前缀字符串到达的位置
2.反向进行遍历,如果此时map里面存在元素,那么map内包含的也就是n-i+1(后面计算过的),如果不存在,就是0;
-
那为什么要反向排列呢?因为反向排列是从后面的元素开始逆推,如果是正向开始,貌似也行哦,只需要重复该步骤就可以了,这里的处理是真的优雅!!!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)