题目链接:https://ac.nowcoder.com/acm/problem/23053
题意:给你一个字符串设为A,接下来有n个串设为B,问你这些这n个串中那些是A的子串。
思路:暴力的想法就是从B的第一个字符开始匹配,匹配到了就同时匹配A和B的下一个字符,否则的话就匹配s的下一个字符直到成功或者到A的最后一个字符。如果B的最后一个字符也匹配成功了,就说明这个是子串,否则就不是。这么做的话太暴力了,最坏复杂度是∣A∣* |B|。肯定不行(嘘,我也不知道为什么这么暴力的答案能AC)。
于是我们就去想,能不能不去做那些无意义的匹配,于是我们开一个next[maxn][26]数组,表示第 i 个字母之后的第一个′a′...′z′分别在哪一个位置,这样每次匹配的复杂度就从最坏的|A|变成了|B|,但是|B|的和是有限的,也就代表整体的复杂度降下来了。
接下来就是想如何写这个next数组了,我们从前往后写肯定爆炸了,因为你要每次都去找下一个′a′...′z′的位置,想想都可怕。于是我们倒着来写,从后往前写。用一个数组 last[26]表示当前位置i往后最靠前的字母′a′...′z′分别在哪,然后把这个数组复制给next[i]就可以了。接下来每次接收到B时,先确定第一个字符在A中的位置,再去找当前位置下 下一个字符的位置就好,直到全部找到或某次寻找中发现它再后面没有出现过。至此代码也就可以写出来了。
//author CN.TTDragon #include<bits/stdc++.h> typedef long long ll; const ll mod=1e9+7; const ll maxn=1e6+7; const double pi=acos(-1); using namespace std; char name1[maxn]={0};//最初的A char name2[maxn]={0}; //每次的B int last[26]={0}; int nx[maxn][26]={0};//next数组 int main() { scanf("%s",name1); //进行处理 int n1=strlen(name1); memset(last,-1,sizeof(last)); //last[name1[n1-1]-'a']=n1-1; for(int i=n1-1;i>=0;i--) { for(int j=0;j<26;j++) { nx[i][j]=last[j];//如果是-1就代表它在后面没出现过 } last[name1[i]-'a']=i; } int t ; scanf("%d",&t); while(t--) { scanf("%s",name2); int n2=strlen(name2); //确定第一个字符的位置 int pla=-1; if(name1[0]==name2[0]) { pla=0; } else { pla=nx[0][name2[0]-'a']; } if(pla==-1)//第一个字符都没有找到,炸了炸了 { printf("No\n"); continue; } int f=1; for(int i=1;i<n2;i++) { pla=nx[pla][name2[i]-'a']; //后面这个字符再也没有出现过 if(pla==-1) { f=0; break; } } if(f) { printf("Yes\n"); } else { printf("No\n"); } } return 0; }
写在最后:
我们仍未知道那个立夏暴力AC的题的原因(皮一下~)