12.11闲话
下雪了,明天不用跑操了,赢
但是别人去打雪仗我来机房写字符串了,输
听说7班有人打雪仗摔了一下给摔失忆了,这么抽象?
学校概率的自助居然有一道期望题,Orz
(就是类似今年S组初赛那个期望再简单亿点)
(大概就是有个转盘然后格子有那个价值,然后算概率再乘上价值再根据\(E(x)+E(y)=E(x+y)\)然后出来了)
人均切了所以九年级人均概率期望大神Orz
今天试图卡常战胜评测机波动造成的90ms字符串匹配但是失败了我的KMP最后也只达到了217ms,输输输
把昨天欠的Hash打完了
//因为我有define int long long的习惯所以下面的int都是ll
const int P1=1e9+7,P2=1e9+9;
int h1[N],h2[N],z1[N],z2[N];
inline void Init(const char *s){
int len=strlen(s+1),b=131;
z1[0]=z2[0]=1;
for(int i=1;i<=len;i++){
(z1[i]=z1[i-1]*b)%=P1;
(z2[i]=z2[i-1]*b)%=P2;
(h1[i]=h1[i-1]*b+(s[i]-'A'+1%P1))%=P1;
(h2[i]=h2[i-1]*b+(s[i]-'A'+1%P2))%=P2;
}
}
inline int Get1(int l,int r){
return (h1[r]-h1[l-1]*z1[r-l+1]%P1+P1)%P1;
}
inline int Get2(int l,int r){
return (h2[r]-h2[l-1]*z2[r-l+1]%P2+P2)%P2;
}
接着复习一下KMP因为快忘了
KMP
KMP用来解决的问题是模式串在文本串的匹配问题
暴力\(O(nm)\)明显不行,然后哈希的\(O(n+m)\)虽然没问题但是因为不稳定所以可能会白给
定义
-
\(Pre_i\)
一个字符串长为 \(i\) 的前缀
-
Border
一个字符串 \(s\) 的
Border
为一个同时是 \(s\) 前后缀的真子串。 -
\(nxt_i\)
当前字符串长 \(i\) 的前缀最长的
Border
的长度。
求\(nxt\)数组
-
暴力求
枚举\(j\in[1,i-1]\)并检查\(A[i-j+1\sim i]\)是否等于\(A[1\sim j]\)
这个算法对于每个\(i\)都枚举了\(i-1\)个非前缀子串
时间复杂度\(O(n^2)\)
然后发现太慢了所以考虑能不能更快
当然能了
-
优化
若\(j_0\)是\(nxt[i]\)的候选项,即满足$$j_0<i且A[i-j_0+1\sim i]=A[1\sim j_0]$$那么小于\(j_0\)的最大的满足以上条件的候选项是\(nxt[j_0]\)
或者可以说\(nxt[j_0]+1\sim j_0-1\)之间所有数都不是\(nxt\)的候选项
当\(nxt[i-1]\)计算完毕之后可以得知\(nxt[i-1]\)的所有候选项为$$nxt[i-1],nxt[nxt[i-1]],nxt[nxt[nxt[i-1]]],...\ $$
而如果一个\(j\)是\(nxt[i]\)的候选项,那么\(j-1\)也必须是\(nxt[j-1]\)的候选项
因此计算\(nxt[i]\)时只要把$$nxt[i-1],nxt[nxt[i-1]]+1,nxt[nxt[nxt[i-1]]]+1,...$$作为选项即可
对于待匹配串\(abababaac\)
模式串 前缀 匹配结果 \(abababaac\) \(abababa\) 匹配失败\(nxt(7)=5\) \(abababaac\) \(ababa\) 匹配失败\(nxt(5)=3\) \(abababaac\) \(aba\) 匹配失败\(nxt(3)=1\)) \(abababaac\) \(a\) 匹配成功\(nxt(1)=0\) 那么\(nxt(8)\)也就等于\(1\)
若在第\(i\)个匹配成功则这个字符串的的\(nxt(x)=nxt(i)+1\)
时间复杂度\(O(n)\)
算法
-
模式串是要查找的串。
-
文本串是被查找的串。
KMP
算法的思路其实和求 \(nxt\) 数组差不多,如果当前两个串失配了,那么在模式串中不断查找 \(nxt[nxt[nxt[\cdots]]]\),直到匹配成功继续向下
代码
直接拿的之前题里的
题面
有\(n\)组询问每次询问一个字符串在另一个字符串里出现的次数
#include<bits/stdc++.h>
#define int long long
const int maxm = 0X66CCFF;
namespace IO{
inline void close(){std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);}
inline void Fire(){freopen(".in","r",stdin);freopen(".out","w",stdout);}
inline int read(){int s = 0,w = 1;char ch = getchar();while(ch<'0'||ch>'9'){ if(ch == '-') w = -1;ch = getchar();}while(ch>='0'&&ch<='9'){ s = s*10+ch-'0';ch = getchar();}return s*w;}
inline void write(int x){char F[200];int tmp=x>0?x:-x,cnt=0;;if(x<0)putchar('-') ;while(tmp>0){F[cnt++]=tmp%10+'0';tmp/=10;}if(cnt==0)putchar('0');while(cnt>0)putchar(F[--cnt]);putchar(' ');}
}
using namespace IO;
int nxt[maxm],f[maxm],n,m,ans;
signed main(){
n=read();
while(n--){
memset(nxt,0,sizeof(nxt));
memset(f,0,sizeof(f));
std::string a,b;
std::cin>>a>>b;
a=' '+a;b=' '+b;
int N=a.size()-1,M=b.size()-1;
nxt[1]=0;ans=0;
for(int i=2,j=0;i<=N;i++){
while(j>0 && a[i]!=a[j+1]) j=nxt[j];
if(a[i]==a[j+1]) j++;
nxt[i]=j;
}
for(int i=1,j=0;i<=M;i++){
while(j>0 && (j==N || b[i]!=a[j+1])) j=nxt[j];
if(b[i]==a[j+1]) j++;
f[i]=j;
if(f[i]==N) ans++;//此时是A在B中的某一次出现
}
write(ans);puts("");
}
}
卡常用的抽象代码
题面
给定若干字符串(这些字符串总长\(<4\times 10^5\) ),在每个字符串中求出所有既是前缀又是后缀的子串长度。
#include<bits/stdc++.h>
#define maxm 400005
namespace IO{
inline void close(){std::ios::sync_with_stdio(false);std::cin.tie(nullptr);}
inline void Fire(){freopen(".in","r",stdin);freopen(".out","w",stdout);}
inline int read(){int s = 0,w = 1;char ch = getchar();while(ch<'0'||ch>'9'){ if(ch == '-') w = -1;ch = getchar();}while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+ch-'0';ch = getchar();}return s*w;}
inline void write(int x){char F[200];int tmp=x>0?x:-x,cnt=0;;if(x<0)putchar('-') ;while(tmp>0){F[cnt++]=tmp%10+'0';tmp/=10;}if(cnt==0)putchar('0');while(cnt>0)putchar(F[--cnt]);putchar(' ');}
}
using namespace IO;
signed main(){
register int nxt[maxm];
register std::stack<int> sta;
close();
std::string a;
while(std::cin>>a){
register int N(a.size());
for(register int i(2);i<=N;++i){
int j(nxt[i-1]);
while(j!=0 && a[j]!=a[i-1])
j=nxt[j];
if(a[j]==a[i-1])nxt[i]=j+1;
else nxt[i]=0;
}
register int j(nxt[N]);
while(j)
sta.push(j),j=nxt[j];
while(!sta.empty())
write(sta.top()),sta.pop();
write(N);
puts("");
}
}