BZOJ4641 基因改造 题解
BZOJ4641 基因改造 题解
题意分析
给你两个字符串,问可以在第一个字符串截取出多少个与第二个字符串排列相同的子串。
思路分析
直接转换为与前一个的位置差多少,就变成了一个比较差值序列是否相等的题目,这样我们可以联想到用哈希来比较。
可是这是一个动态的问题,不能直接用普通的哈希,所以我们可以用线段树维护动态的哈希,再配套尺取,即可解决。
当然这不是正解,不过正解只需进一步优化。
发现这种思路的瓶颈是维护一个动态的哈希,那我们换一种不用动态维护的方法,即去除尺取与更改,始终保持处理出来的位置差不变。
这样哈希是做不了的,因为它是一个整体的比较,那我们就换用前缀数组,用变种的 KMP 来比较(俗称 KMP 的重定义)。
具体的实现并不难,略有一些麻烦的细节,直接看代码会更好理解。
CODE
骗分解法
时间复杂度 \(O(case \cdot (n-m)\log_2{n})\)(说实话,不卡常真的过不了,所以我把快读也留下了),空间复杂度 \(O(n)\)。
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define ull unsigned long long
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)>(b)?(b):(a))
#define tomax(a,b) ((a)=max((a),(b)))
#define tomin(a,b) ((a)=min((a),(b)))
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define EDGE(g,i,u,x) for(register int i=(g).h[(u)],x=(g).v[(i)];(i);(i)=(g).nxt[(i)],(x)=(g).v[(i)])
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
namespace IOstream {
//#define Local
#define MAX (1<<20)
#define isdigit(x) ('0'<=(x)&&(x)<='9')
#define blank(x) ((x)==' '||(x)=='\n'||(x)=='\r'||(x)=='\t'||(x)=='\0'||(x)==(EOF))
#define Same(Type) (is_same<typename decay<T>::type,Type>::value)
struct Istream {
#ifdef Local
#define getc() getchar()
#else
char *p1,*p2,buf[MAX];
Istream():p1(buf),p2(buf) {}
~Istream() {}
inline char getc() {
return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAX,stdin),p1==p2)?(EOF):*p1++;
}
#endif
inline int rd(string &x) {
char ch=getc();
for(x.clear(); blank(ch); ch=getc())if(ch==(EOF))return (EOF);
for(; !blank(ch); x+=ch,ch=getc())if(ch==(EOF))return (EOF);
return 0;
}
template<typename T>inline enable_if_t<Same(char*),int>rd(T &x) {
unsigned i=0;
char ch=getc();
for(; blank(ch); ch=getc())if(ch==(EOF))return (EOF);
for(; !blank(ch); *(x+i)=ch,++i,ch=getc())if(ch==(EOF))return *(x+i)=0,(EOF);
return *(x+i)=0,0;
}
template<typename T>
inline enable_if_t<Same(short)||Same(int)||Same(long long)||Same(__int128),int>rd(T &x) {
bool sign=0;
char ch=getc();
for(x=0; !isdigit(ch); sign|=(ch=='-'),ch=getc()) if(ch==(EOF))return (EOF);
for(; isdigit(ch); ch=getc())x=(x<<3)+(x<<1)+(ch^48);
return x=(sign?-x:x),0;
}
template<typename T>
inline enable_if_t<Same(unsigned short)||Same(unsigned int)||Same(unsigned long long)||Same(unsigned __int128),int>rd(T &x) {
char ch=getc();
for(x=0; !isdigit(ch); ch=getc())if(ch==(EOF))return (EOF);
for(; isdigit(ch); ch=getc())x=(x<<3)+(x<<1)+(ch^48);
return 0;
}
template<typename T>
inline enable_if_t<Same(char)||Same(unsigned char),int>rd(T &x) {
for(x=getc(); blank(x); x=getc())if(x==(EOF))return (EOF);
return 0;
}
template<typename T,typename... Types>inline int rd(T &x,Types&... args) {
return rd(x)==(EOF)?(EOF):rd(args...);
}
} Cin;
struct Ostream {
#ifdef Local
#define putc(x) putchar(x)
#else
char *pp,pbuf[MAX];
Ostream():pp(pbuf) {}
~Ostream() {
fwrite(pbuf,1,pp-pbuf,stdout);
}
inline void flush() {
fwrite(pbuf,1,MAX,stdout),pp=pbuf;
}
inline void putc(const char &c) {
*pp++=c;
if(pp-pbuf==MAX)flush();
}
#endif
inline void wr(const string &x) {
for(char ch:x)putc(ch);
}
template<typename T>
inline enable_if_t<Same(char*)||Same(const char*),void>wr(const T &x) {
for(unsigned i=0; *(x+i); ++i)putc(*(x+i));
return;
}
template<typename T>
inline enable_if_t<Same(short)||Same(int)||Same(long long)||Same(__int128),void>wr(const T &x) {
T cur=x;
if(cur<0)cur=-cur,putc('-');
static short sta[105],top(0);
do sta[++top]=cur%10,cur/=10;
while(cur);
while(top)putc(sta[top--]|48);
return;
}
template<typename T>
inline enable_if_t<Same(unsigned short)||Same(unsigned int)||Same(unsigned long long)||Same(unsigned __int128),void>wr(const T &x) {
T cur=x;
static short sta[105],top(0);
do sta[++top]=cur%10,cur/=10;
while(cur);
while(top)putc(sta[top--]|48);
return;
}
template<typename T>
inline enable_if_t<Same(char)||Same(unsigned char),void>wr(const T &x) {
return putc(x),void();
}
template<typename T,typename... Types>inline void wr(const T &x,const Types&... args) {
return wr(x),wr(args...);
}
} Cout;
#undef MAX
#undef isdigit
#undef blank
#undef Same
} using namespace IOstream;
constexpr int N=1e6+10,P=13331,M=1e9+7;
int Cas,C,n,m,tot;
int a[N],b[N],q[N],nxt[N],pre[N],last[N];
ull Hash;
ull pw[N];
struct SEG{
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((tr[p].l+tr[p].r)>>1)
struct node{
int l,r;
ull H;
}tr[N<<2];
void Up(int p){
tr[p].H=tr[ls].H*pw[tr[rs].r-tr[rs].l+1]+tr[rs].H;
}
void Build(int p,int l,int r){
tr[p]={l,r,0};
if(l==r)return tr[p].H=pre[l],void();
Build(ls,l,mid),Build(rs,mid+1,r),Up(p);
}
void Update(int p,int x,ull d){
if(tr[p].l==tr[p].r)return tr[p].H=d,void();
Update(x<=mid?ls:rs,x,d),Up(p);
}
ull Query(int p,int l,int r){
if(l<=tr[p].l&&tr[p].r<=r)return tr[p].H;
if(r<=mid)return Query(ls,l,r);
if(mid<l)return Query(rs,l,r);
ull L=Query(ls,l,r),R=Query(rs,l,r);
return L*pw[min(tr[p].r,r)-mid]+R;
}
#undef ls
#undef rs
#undef mid
}seg;
signed Cmain(){
Cin.rd(n,m),tot=0;
FOR(i,1,n)Cin.rd(a[i]);
FOR(i,1,m)Cin.rd(b[i]);
RCL(last,0,last,1),Hash=0ull;
FOR(i,1,m)Hash=Hash*P+(!last[b[i]]?M:i-last[b[i]]),last[b[i]]=i;
RCL(last,0,last,1);
FOR(i,1,n)pre[i]=(!last[a[i]]?M:i-last[a[i]]),last[a[i]]=i;
RCL(last,0,last,1);
DOR(i,n,1)nxt[i]=last[a[i]],last[a[i]]=i;
seg.Build(1,1,n);
FOR(l,1,n-m+1){
if(seg.Query(1,l,l+m-1)==Hash)q[++tot]=l;
if(nxt[l])seg.Update(1,nxt[l],M);
}
Cout.wr(tot);
if(tot)puts("");
FOR(i,1,tot)Cout.wr(q[i],' ');
puts("");
return 0;
}
signed main(){
pw[0]=1ull;
FOR(i,1,N-10)pw[i]=pw[i-1]*P;
for(Cin.rd(Cas,C);Cas;--Cas)Cmain();
return 0;
}
正解
时间复杂度 \(O(case \cdot n)\),空间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)>(b)?(b):(a))
#define tomax(a,b) ((a)=max((a),(b)))
#define tomin(a,b) ((a)=min((a),(b)))
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define EDGE(g,i,u,x) for(register int i=(g).h[(u)],x=(g).v[(i)];(i);(i)=(g).nxt[(i)],(x)=(g).v[(i)])
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
constexpr int N=1e6+10;
int Cas,C,n,m,tot;
int a[N],b[N],ans[N],last[N];
void Next(int n,int *a,int *nxt){
int it=-1;
nxt[0]=-1;
FOR(i,1,n){
for(;~it&&a[i]!=a[it+1]&&!(a[i]>it&&a[it+1]==-1);it=nxt[it]);
//当作为一个没出现过的数时,原条件无法更新,故需重新特判
nxt[i]=++it;
}
}
void Match(int n,int m,int *a,int *b){
int it=0;
static int nxt[N];
Next(m,b,nxt),tot=0;
FOR(i,1,n){
for(;~it&&a[i]!=b[it+1]&&!(a[i]>it&&b[it+1]==-1);it=nxt[it]);
if((++it)==m)ans[++tot]=i-m+1,it=nxt[it];
}//匹配时也是同理
}
signed Cmain(){
cin>>n>>m;
RCL(last,0,last,1);
FOR(i,1,n){
int x;
cin>>x,a[i]=(last[x]?i-last[x]:-1),last[x]=i;//没出现过时,标为 -1,以便后面判断
}
RCL(last,0,last,1);
FOR(i,1,m){
int x;
cin>>x,b[i]=(last[x]?i-last[x]:-1),last[x]=i;
}
Match(n,m,a,b);
cout<<tot;
if(tot)cout<<endl;
FOR(i,1,tot)cout<<ans[i]<<" ";
cout<<endl;
return 0;
}
signed main(){
for(cin>>Cas>>C;Cas;--Cas)Cmain();
return 0;
}

浙公网安备 33010602011771号