以cf501的B (可能记错)和百度之星2018 的1002为例
嘛 在字符串里面有一个kmp很重要(但是我还不太会)
此外就是这个前缀和 我们知道,每个字母只有26个呀
所以即使带上全部的状态存,也不会很大,1e5只有1e5*26
那么如何分析呢?
首先,cf的题是求某个区间里面子串的个数,查询有1e5次,那么直接从1开始往后加到n存下来,用的时候直接减去左边就可以了。
有个小地方就是,减去的不是l,而是l+m(m是子串的长度)
因为区间一共就那么长,前面都没到既定长度,不会出现子串的。
这样做就避免了aaaaaaaa里面aaa的问题。
(最开始自己写的时候还弄了个后缀和,傻死了,不要妄想着特殊特殊特殊最后就过了呀,如果遇到自己发现的一组奇怪的样例,应该分析一下哪里导致了问题呀,或者样例多试试几次,手写一下的话其实也能发现有问题的)
其中字符串匹配最好还是用kmp,自己手写了一个好像有时候会碰到麻烦,反正wa9了,先扔到这里吧,……面对aaab和aab的时候其实应该从2开始算,后面的cls记录又乱了,也没再改【所以说去学kmp啊!!!】
注意一下l到r应该是r-l+1 还有字符串匹配的+1小问题【】
百度之星这题猛一看没什么思路,其实是一样的,这里问的是在一个大串串里,最小的数的出现的次数
其实。。。。。
思路猛然打开就会发现,还是前缀和,用一个二维sum数组存好,(不复杂)然后查询的时候每次先揪出这次是哪个,再输出它的出现次数就好了。
【注意是A-‘A’+1】
还有,我蠢到aaa实验了半天样例,发现全错了。。。 这题是大写的,小写习惯了。。。
【话说感觉在纸上写出来再实现,优化了不少诶,。。既然t了那大概总会有更简单的地方吧,忽然想到最后查询区间的时候一个个跑过去也很浪费,那么直接从小到大看哪个不是0就输出哪里好了,最后复杂度只有1e5*26+q*26】
ps 百度之星这个vs里面好像开不了这么大的数组
wa了竟然是因为没加case 所以说了嘛 看样例看样例看样例 这种年代怎么还出现没测样例就扔上去的惨案啊…
以及我用的cin.. 加了一句神奇的东西
或者你可以直接cin.sync_with_stdio(0);来加速cin(但是不能使用scanf了)
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
int main() {
ios::sync_with_stdio(0);
int t;
//char ccc; cin >> ccc;
//cout << ccc - 'A'+1 << endl;
cin >> t; int kase = 0;
while (t--) {
int n, q; cin >> n >> q;
int a[100005];
char c[100005];
int sum[100005][30];
for(int i=1;i<=26;i++)
sum[0][i] = 0;
c[0] = ' ';//啊啊啊啊啊啊啊这里要用' '
for (int i = 1; i <= n; i++)//scanf("%s",c+1);
cin >> c[i];
//先测试是什么
for (int i = 1; i <= n; i++) {
//cout << c[i] - 'A' +1<<"---------"<<endl;
sum[i-1][c[i] - 'A' +1]++;// ta ++
//cout << sum[i - 1][c[i] - 'A' +1]<<endl;
for (int j = 1; j <= 26; j++)
sum[i][j] = sum[i-1][j];
sum[i - 1][c[i] - 'A' + 1]--;
//cout << sum[i][c[i] - 'A' +1] << endl;
//cout << "-------" << endl;
}cout << "Case #" << ++kase << ":" << endl;
while (q--) {
int aa, bb,cc; cin >> aa >> bb;
if (aa == bb)cout << "1" << endl;
else {//所谓优化 就是减少浪费啊
for (int i = 1; i <= 26; i++) {
// cc c[i] - 'A' +1));
//cout << cc << endl;
//for (int i = 1; i <= n; i++)cout << sum[i][cc] << " ";
//cout << endl;
if (sum[bb][i] - sum[aa - 1][i] != 0)
{
cout<< sum[bb][i] - sum[aa - 1][i] << endl;
break;
}
}
}
}
}
return 0;
}
(怎么写的这么丑。。)
420ms.. 直接暴力改了kmp.. 这个以后当板子用吧=-=
// 其实我写的那个每个地方存好cnt[i]的也是不再增加的...
//感觉我写的那个也有点线性吧-.-
// 就是cls那里的关系没搞清楚 然后各个地方经常容易错,现在用kmp再来写一遍
//看别人的, 感觉直接对区间进行kmp的每个查询也能过
//或者,他们的预处理是分开的预处理 , 对每个地方都预处理一下
//我的是区间直接用的
#include<stdio.h>
#include<string>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int rec[1005];
int n,m,aaa;
int nnext[1005];
char P[1005];
char T[1005];
void makeNext()
{
// memset(next,0,sizeof(next));
//... 每个地方都用到了的话 应该还好吧
int q,k;
nnext[0] = 0;
for (q = 1,k = 0; q < m; ++q)
{
while(k > 0 && P[q] != P[k])
k = nnext[k-1];
if (P[q] == P[k])
{
k++;
}
nnext[q] = k;
}
}
int kmp(int j)
{
int cnt=0;
int q=0;
makeNext();
for (int i = 0; i < j; ++i)
{
while(q > 0 && P[q] != T[i])
q = nnext[q-1];
if (P[q] == T[i])
{
q++;
}
if (q == m)
{
cnt++;
}
} return cnt;
}
int main()
{ cin>>n>>m>>aaa;
scanf("%s",T);
scanf("%s",P);
for(int i=0;i<=n;i++){
rec[i]=kmp(i);
}
// for(int i=1;i<=n;i++)cout<<rec[i];
// cout<<endl;
int l,r;
while(aaa--){
cin>>l>>r;
if(r-l+1<m)cout<<"0"<<endl;
else
cout<<max(0,rec[r]-rec[l+m-2])<<endl;
}
return 0;
}
已经很暴力了… 注意一下如果l和r之间如果没到那个关系, 可能会出现负的,关键是l+m了啊,但是,是l-1加的m,因为是l之前的,所以是l-1,但是这里又是区间,区间问题格外要注意,区间是5-7 ,长度要7-5+1
所以m是长度,长度是3,区间跨度是2,最后就是l+m-2
留意一下吧,关键是rec[i]的话 不会可以输出一下
然后kmp的地方,我看着自己之前的那个博客,自己手写模拟了一下过程
。。。 图片传布上来
我的天啊。。。 经过一点改进,j是要算的长度、、、
n是大的长度 m是小的长度
//注意 传入的j是长度
//然后从0开始是因为字符串的限制
//最后的rec[i] 是从1开始的
void makeNext()
{
// memset(next,0,sizeof(next));
//... 每个地方都用到了的话 应该还好吧
int k=0;
nnext[0] = 0;
for (int q = 1; q < m; ++q)
//这m是长度
{
while(k > 0 && P[q] != P[k])
k = nnext[k-1];
if (P[q] == P[k])
{
k++;
}
nnext[q] = k;
}
}
int kmp(int j)
{
int cnt=0;
int q=0;
makeNext();
for (int i = 0; i < j; ++i)
{
while(q > 0 && P[q] != T[i])
q = nnext[q-1];
if (P[q] == T[i])
{
q++;
}
if (q == m)
{
cnt++;
}
} return cnt;
}
最后面这个就别看了
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
int rec[1005];
int after[1005];
int main(){
int n, m, q; cin >> n >> m >> q;
int cnt;
string a, b; cin >> a>>b;
int l, r;
if (m != 1 && n > m) {
int j = 0; cnt = 0;
int cls=0;
//问题就是这个-1... -1 -1 -1
for (int i = 0; i < n; i++) {
if (a[i] != b[j]) j = 0;
//一个不相等了 肯定都要等于0 的
// i加了 j不变
else if (a[i] == b[j]) j++;
//开始是0 完了++是1 最后++是2
//相等的话 j要加 i也要加 继续往后比
if (j == m)// 走完了..
{
cnt++;
j = 0;
rec[i] = cnt;
cls = m - 1;//偏移量
i = i - m + 1;
}
//如果没走完
else {// 如果cls大于0的话说明前面是偏移来的....
if (cls == 0)
rec[i] = cnt;
else cls--;
}
}
/*
j = m-1; cnt = 0;
cls = 0;
//问题就是这个-1... -1 -1 -1
for (int i = n-1; i >=0; i--) {
if (a[i] != b[j]) j =m-1 ;
//一个不相等了 肯定都要等于0 的
// i加了 j不变
else if (a[i] == b[j]) j--;
//开始是0 完了++是1 最后++是2
//相等的话 j要加 i也要加 继续往后比
if (j == -1)// 走完了..
{
cnt++;
j = m-1;
after[i] = cnt;
cls = m - 1;//偏移量
i = i + m - 1;
}
//如果没走完
else {// 如果cls大于0的话说明前面是偏移来的....
if (cls == 0)
after[i] = cnt;
else cls--;
}
}*/
}
//for (int i = 0; i <n; i++)cout << rec[i];
//cout << endl;
//for (int i = 0; i <n ; i++)cout << after[i];
while (q--) {
bool flag = true;
cin >> l >> r;
if (n < m)cout << "0" << endl;
else if (m == 1) {
int cnt = 0;
for (int i = l-1; i <= r-1; i++) {
if (a[i] == b[0])cnt++;
}cout << cnt << endl;
}
else {
// 记得要-1 -1 -1
//if (rec[l] == 0 )cout << rec[r-1]-rec[l-1] << endl;
r--; l--;
if(r-l+1>=m)
cout <<max(rec[r]-rec[l+m-2],0)<< endl;
else cout << "0" << endl;
}
}
return 0;
}