题目
题目描述:
程序猿圈子里正在流行一种很新的简写方法:对于一个字符串,只保留首尾字符,将首尾字符之间的所有字符用这部分的长度代替。例如 internation-alization 简写成 i18n,Kubernetes (注意连字符不是字符串的一部分)简写成 K8s, Lanqiao 简写成 L5o 等。
在本题中,我们规定长度大于等于 K 的字符串都可以采用这种简写方法(长度小于 K 的字符串不配使用这种简写)。
给定一个字符串 S 和两个字符 c1 和 c2,请你计算 S 有多少个以 c1 开头c2 结尾的子串可以采用这种简写?
输入格式:
第一行包含一个整数 K。
第二行包含一个字符串 S 和两个字符 c1 和 c2。
输出格式:
一个整数代表答案。
样例输入:
4
abababdb a b样例输出:
6
提示:
符合条件的子串如下所示,中括号内是该子串:
[abab]abdb
[ababab]db
[abababdb]
ab[abab]db
ab[ababdb]
abab[abdb]
对于 20% 的数据,2 ≤ K ≤ |S | ≤ 10000。
对于 100% 的数据,2 ≤ K ≤ |S | ≤ 5 × 105。S 只包含小写字母。c1 和 c2 都是小写字母。
|S | 代表字符串 S 的长度。
题解:
用数组arr记录当前位置前面出现过的首字母次数,t 表示出现首字母的次数,pre表示上一个首字母出现的位置。
遍历每一个字母:
如果是首字母:首字母出现次数++,更新数组当前位置和上一次出现的次数pre;arr[i]=t; pre=i;
如果是尾字母:如果pre=-1则前面还没有首字母直接跳过;如果pre到当前位置的距离小于k-1,不符合规则,就从距离k-1的位置往前遍历找首字母,找到后就总次数res+前面有的首字母个数arr[j];如果pre到当前位置大于等于k-1,则符合规则,直接加上前面有的首字母个数。
样例图解
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
long long arr[N]; //记录当前位置 i 前面出现过几个首字母
int main()
{
int k; cin >> k;
string s;
char c1, c2;
cin >> s >> c1 >> c2;
int pre = -1, t = 0; //pre上一个出现首字母的位置,t首字母出现的次数
long long res = 0;//符合规则的子串总数
for(int i = 0; i <= s.size(); i++){
if(s[i] == c1){//当前字母是首字母
t++; pre = i;
arr[i] = t;
}
if(s[i] == c2){//当前字母是尾字母
if(pre == -1)continue;
if(i - pre >= k - 1){
res += arr[pre];
}else{
int j = i - k + 1;
while(j >= 0 && s[j] != c1)j--;//往前找首字母
if(j >= 0 && s[j] == c1){
res+= arr[j];
}
}
}
}
cout << res;
return 0;
}