题目
给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模式串 P 在字符串 S 中多次作为子串出现。
求出模式串 P 在字符串 S 中所有出现的位置的起始下标。
输入格式
第一行输入整数 N,表示字符串 P 的长度。
第二行输入字符串 P。
第三行输入整数 M,表示字符串 S 的长度。
第四行输入字符串 S。
输出格式
共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。
数据范围
1≤N≤1e5
1≤M≤1e6
输入样例:
3
aba
5
ababa
7
ABABABC
3
ABA
输出样例:
0 2
代码
#include <iostream>
using namespace std;
const int N = 100010;
int q[N];
int a[N];
int n,k;
int main()
{
//输入数组的长度 和滑动窗口的长度
cin >> n >> k;
for (int i = 0; i < n; i++) cin >> a[i];
int hh = 0, tt = -1;
for (int i = 0; i < n; i++)
{
//首先判断队头是否还在滑动窗口里面
//如果不在 hh++
while ( tt >= hh && i - k + 1> q[hh]) hh++;
//如果下一个元素要大于当亲的队尾元素 则tt--
while (tt >= hh && a[i] <= a[q[tt]]) tt--;
q[++tt] = i;
if (i - k + 1 >= 0) cout << a[q[hh]] << ' ';
}
puts("");
hh = 0, tt = -1;
for (int i = 0; i < n; i++)
{
//首先判断队头是否还在滑动窗口里面
//如果不在 hh++
while (tt >= hh && i - k + 1 > q[hh]) hh++;
//如果下一个元素要大于当亲的队尾元素 则tt--
while (tt >= hh && a[i] >= a[q[tt]]) tt--;
q[++tt] = i;
if (i - k + 1 >= 0) cout << a[q[hh]] << ' ';
}
return 0;
}
总结
只要代码敲的勤快 就越写越快 又快有对
题目:trie字符串统计
维护一个字符串集合,支持两种操作:
1、 I x向集合中插入一个字符串 x;
2、 Q x询问一个字符串在集合中出现了多少次。
共有 N个操作,所有输入的字符串总长度不超过10^5 ,字符串仅包含小写英文字母。
输入格式
第一行包含整数 N,表示操作数。
接下来 N行,每行包含一个操作指令,指令为 I x 或 Q x 中的一种。
输出格式
对于每个询问指令 Q x,都要输出一个整数作为结果,表示 x在集合中出现的次数。
每个结果占一行。
数据范围
1≤N≤2∗10^4
输入样例:
5
I abc
Q abc
Q ab
I ab
Q ab
输出样例:
1
0
1
#include <iostream>
using namespace std;
const int N = 10010;
char str[N];
//储存trie树
int s[N][26];
int cnt[N];//表设计
int idx; //表示当前匹配到的位置
void insert(char *str)
{
//从顶点开始寻找
int p = 0;
//从字符串的第一个开始往下来寻找
for (int i = 0; str[i]; i++)
{
//先把该位置的字符变成数字
int n = str[i] - 'a';
//当前字符不存在的话 创建一个
if (!s[p][n]) s[p][n] = ++idx;
p = s[p][n];
}
cnt[p] ++ ;// 以该数字为结尾的字符加一
}
int query(char *str)
{
int p = 0;
for (int i = 0; str[i]; i++)
{
int n = str[i] - 'a';
if (!s[p][n]) return 0;
p = s[p][n];
}
return cnt[p];
}
int main()
{
int n;
cin >> n;
while (n--)
{
char op[2];
cin >> op >> str;
if (op[0] == 'I')
insert(str);
else cout << query(str);
}
return 0;
}
题目:合并集合
一共有 n个数,编号是 1 ∼ n ,最开始每个数各自在一个集合中。
现在要进行 m个操作,操作共有两种:
1、 M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
2、 Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中;
输入格式
第一行输入整数 n 和 m 。
接下来 m 行,每行包含一个操作指令,指令为M a b或Q a b中的一种。
输出格式
对于每个询问指令Q a b,都要输出一个结果,如果 a和 b 在同一集合内,则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1 ≤ n ,m≤10^5
输入样例:
4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4
输出样例:
Yes
No
Yes
代码
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int p[N];
//找到数字的头节点
int find(int o)
{
//如果o不是父节点 就让父节点等于祖宗节点
if (p[o] != o) p[o] = find(p[o]);
return p[o];
}
int main()
{
cin >> n >> m;
//将头结点都存进p数组中
for (int i = 1; i <= n; i++) p[i] = i;
while (m--)
{
char op[2];
int x, y;
cin >> op>> x >> y;
if (op[0] == 'M')
{
//x的祖宗节点的父亲等于 y的祖宗节点
if (find(x) != find(y)) p[find(x)] = find(y);
}
else {
if (find(x) == find(y)) cout << "YES\n" ;
else cout << "NO\n";
}
}
return 0;
}