【校内模拟】耍望节(倍增)(KMP)

17 篇文章 0 订阅
9 篇文章 0 订阅

简要题意:

给你一个残缺的串 S S S,里面有一些空位,你需要填充里面的空位使得 T T T S S S的字串。

多次询问字典序第 k k k小的填充方案,输出哈希值。

题解:

倍增裸题。

反正我这辈子都在考场上写不出字典序倍增

考虑建立KMP自动机,状态 [ i ] [ j ] [i][j] [i][j]表示已经决定了前 i − 1 i-1 i1个字符的填充,当前匹配到了第 j j j个字符。

选择siz最大的节点作为后继节点。

预处理倍增后在KMP自动机的位置,倍增需要跳的rk,倍增过程中的哈希值。

然后大力倍增就行了。

估计没有谁跟我一样考场上写了结构体没调出来。。。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	static cs int Rlen=1<<22|1;
	static char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	inline char pk(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1;}
	int get_s(char *s){
		int len=0;char c;while(isspace(c=gc()));
		while(s[len++]=c,!isspace(c=gc())&&c!=EOF);
		return s[len]='\0',len;
	}
	char gs(){while(isspace(pk()))++p1;return *p1++;}
	template<typename T>T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using namespace IO;

using std::cerr;
using std::cout;

template<typename T>void ckmin(T &a,T b){a>b?a=b:0;}

cs int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

cs int N=5e4+7;
cs ll INF=1e18+1e7;
int pw[N],n,q,m;
char s[N],t[N];
int nxt[22];//KMP 
int to[22][22];//KMP自动机 
int suf[N];//后缀哈希值 
int c0[N];
std::vector<int> vec[N],zero;
struct state{
	ll rk[17];int h[17],ps[17],lim;
	//倍增跳过的排名,倍增过程里面的哈希值,倍增后在KMP自动机上面的位置
	ll siz;//有多少种合法的后继决策能够到达KMP终止状态,与INF取min
	ll pre[13];//走每个孩子要减掉的排名有多少
	int son;
}st[N][22];
bool vis[N][22];
//到了第i个位置,第i个位置还没有决策,当前在KMP自动机的第j的位置上面的状态转移 

//找到第一个能够接纳它的KMP终止位置,然后按照排名在后面空位填数
//倍增过程除了最后一步,其余都不经过KMP终止位置。 
void dfs(int i,int j){//当前位置,在KMP上的位置
	if(vis[i][j])return ;vis[i][j]=true;
	if(i>n)return ;
	if(j==m){
		st[i][j].siz=pow(10,std::min(18,c0[i]));
		for(int re k=0;k<17;++k)st[i][j].rk[k]=INF;
		return ;
	}
	int son=0;ll siz=0;
	for(int re c=0;c<vec[i].size();++c){
		int t=to[j][vec[i][c]];dfs(i+1,t);
		ll pre=st[i][j].siz,now;st[i][j].pre[c]=pre;
		ckmin(st[i][j].siz+=st[i+1][t].siz,INF);
		now=st[i][j].siz-pre;
		if(now>siz)son=c,siz=now;
	}st[i][j].son=vec[i][son];
	if(st[i][j].siz==0)return ;
	st[i][j].rk[0]=st[i][j].pre[son];
	st[i][j].h[0]=mul(vec[i][son],pw[n-i]);
	st[i][j].ps[0]=to[j][vec[i][son]];
	for(int re p=1;p<=16;++p){
		int t=st[i][j].ps[p-1],k=i+(1<<(p-1));
		if(k>n+1)break;
		st[i][j].rk[p]=std::min(st[i][j].rk[p-1]+st[k][t].rk[p-1],INF);
		st[i][j].h[p]=add(st[i][j].h[p-1],st[k][t].h[p-1]);
		st[i][j].ps[p]=st[k][t].ps[p-1];
	}
} 

void work(int i,int j,ll rk,int hs){
	if(rk>st[i][j].siz){cout<<"-1\n";return ;}
	for(int re p=16;~p&&j!=m;--p){
		int t=st[i][j].ps[p],k=i+(1<<p);if(k>n+1)continue;
		if(st[i][j].rk[p]<rk&&st[i][j].rk[p]+st[k][t].siz>=rk){
			rk-=st[i][j].rk[p],Inc(hs,st[i][j].h[p]),i=k,j=t;
		}
	}
	if(j==m){
		Inc(hs,suf[i]);--rk;
		for(int re c=zero.size()-1;~c&&zero[c]>=i&&rk;--c){
			Inc(hs,mul(rk%10,pw[n-zero[c]]));
			rk/=10;
		}
		cout<<hs<<"\n";return ;
	}
	for(int re c=0;c<vec[i].size();++c){
		int t=to[j][vec[i][c]];
		if(st[i][j].pre[c]<rk&&st[i+1][t].siz+st[i][j].pre[c]>=rk){
			return work(i+1,t,rk-st[i][j].pre[c],add(hs,mul(vec[i][c],pw[n-i])));
		}
	}
} 

void solve(){
	n=gi(),q=gi();zero.clear();
	m=get_s(t+1);get_s(s+1);
	for(int re i=1;i<=n;++i){
		vec[i].clear();
		if(s[i]!='?'){
			if(i==1&&s[i]=='0'){
				while(q--)gl(),cout<<"-1\n";
				return ;
			}
			vec[i].push_back(s[i]^48);
			c0[i]=0;
		}else {
			for(int re j=(i==1);j<=9;++j)vec[i].push_back(j);
			zero.push_back(i);c0[i]=1;
		}
	}suf[n+1]=0; 
	for(int re i=n;i;--i){
		int val=vec[i].size()==1?vec[i].back():0;
		suf[i]=add(suf[i+1],mul(val,pw[n-i]));
		c0[i]+=c0[i+1];
	}
	for(int re i=2,j=0;i<=m;++i){
		while(j&&t[j+1]!=t[i])j=nxt[j];
		if(t[j+1]==t[i])++j;nxt[i]=j;
	}memset(to,0,sizeof to);
	to[0][t[1]^48]=1;
	for(int re i=1,k;i<=m;++i){k=i;
		while(k){
			if(!to[i][t[k+1]^48])
			to[i][t[k+1]^48]=k+1;
			k=nxt[k];
		}
		if(!to[i][t[k+1]^48])
			to[i][t[k+1]^48]=k+1;
	}
	for(int re i=1;i<=n+1;++i)memset(st[i],0,sizeof st[i]);
	st[n+1][m].siz=1;memset(vis,0,sizeof vis);
	dfs(1,0);while(q--)work(1,0,gl(),0);
} 

signed main(){
#ifdef zxyoi
	freopen("shuawang.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("shuawang.in","r",stdin);freopen("shuawang.out","w",stdout);
#endif
#endif
	pw[0]=1;for(int re i=1;i<N;++i)pw[i]=mul(pw[i-1],10);
	int T=gi();while(T--)solve();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值