2020牛客NOIP赛前集训营-提高组(第二场)C 前缀

R e s u l t Result Result

...


H y p e r l i n k Hyperlink Hyperlink

https://ac.nowcoder.com/acm/contest/7607/C


D e s c r i p t i o n Description Description

n n n组询问,每次给出一个 t t t,再给定一个无限长的字符串,它的循环节是 s s s,试求这个串的一个最短前缀 s ‘ s^` s,使得 t t t s ‘ s^` s的一个子序列。其中 t t t的某些位置可能是一个星号,表示这个位置可以是任意字符。

数据范围: ∣ s ∣ ≤ 1 0 4 , t 的真正长度 ≤ 1 0 1 0 5 |s|\leq 10^4,t\text{的真正长度}\leq 10^{10^5} s104,t的真正长度10105


S o l u t i o n Solution Solution

t o t i tot_i toti表示原串中某个字符的个数, q z i , j qz_{i,j} qzi,j表示 s s s的前 i i i位, j j j字符的出现次数
a p p e a r i , j appear_{i,j} appeari,j表示 s s s串中 i i i字符第 j j j次出现的位置

考虑计算循环了几个 s s s—— x h xh xh
考虑循环后需要某个字符多少个—— n e e d need need(当 s t i ≠ st_i\neq sti=星号时,这个字符就是 s t i st_i sti,否则这个字符可以是任意字符)
考虑上次循环的结束位置—— l a s t last last
对于输入的数很大的话,要用类似于高精度的方法对其进行处理

最后统计答案即可
时间复杂度: O ( 26 ∣ s ∣ + n ∣ t ∣ ) O(26|s|+n|t|) O(26s+nt)


C o d e Code Code
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define mod 998244353
using namespace std;char s[10010],st[100010],ybd;
int len,n,appear[26+150][10010];
LL xh,need,tot[26+150],qz[10010][26+150],once,ans,last;
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline LL ksm(LL x,LL y)
{
	LL res=1;
	for(;y;y>>=1,(x*=x)%=mod) 
	if(y&1) (res*=x)%=mod;
	return res;
}
inline bool tp()
{
	for(register int i=1;i<=strlen(st+1);i++) if(st[i]!='*'&&!isdigit(st[i])&&tot[st[i]]==0) return true;
	return false;
}
signed main()
{
	scanf("%s",s+1);len=strlen(s+1);s[len+1]=s[1];
	for(register int i=1;i<=len;i++) 
	{
		tot[s[i]]++;
		memcpy(qz[i],qz[i-1],sizeof(qz[i-1]));
		appear[s[i]][++appear[s[i]][0]]=i;
		qz[i][s[i]]++;
	}
	n=read();
	while(n--)
	{
		scanf("%s",st+1);
		if(tp()) {puts("-1");continue;}
		ans=0;last=1;
		for(register int i=1;i<=strlen(st+1);i++)
		{
			ybd=st[i];
			once=st[i]=='*'?len:tot[st[i]];
			xh=0;need=0;
			if(isdigit(st[i+1]))
			{
				while(isdigit(st[i+1]))
				{
					xh=((xh<<3)+(xh<<1))%mod;
					need=((need<<3)+(need<<1)+st[++i]-48)%mod;
					(xh+=need/once)%=mod;need%=once;
				}
			}
			else need=1;
			if(ybd=='*') need+=once-(len-last+1);//加上上次做完后剩下的部分
			else need+=once-(tot[ybd]-qz[last-1][ybd]);//同理
			if(need>once) need-=once,xh++;//超过了一次,则构成一个循环
			if(xh==0) xh=mod;//没有循环,直接让它=mod
			if(need==0) need+=once,xh--;//不需要字符,则让它加上一次慢的,同时令循环少1
			if(ybd!='*')//不是星号
			{
				ans+=appear[ybd][need]-last+1;//补齐上次的
				last=appear[ybd][need]%len+1;//同理
			}
			else//是星号的话,则任意字符都可以
			{
				ans+=need-last+1;
				last=need%len+1;
			}
			(ans+=len*xh%mod)%=mod;//加上循环的贡献
		}
		printf("%lld\n",ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值