传送门
牛牛有一个 s s s串, s s s串仅由 26 26 26个小写英文字母组成,他现在将 s s s串进行了无限次的复制扩展成了一个无限循环串。 例如一开始 s = “ a b c ” s=“abc” s=“abc”,那么牛牛就会将其变为 “ a b c a b c a b c . . . ” “abcabcabc...” “abcabcabc...” 若某个字符串保留其原本字符出现的顺序,并且按照顺序取出若干个字符。可以不连续,可以不取。 我们称取出的这若干个字符连成的字符串为一个子序列。 若连续取出某个字符串的前 k k k个字符,组成一个子串,我们称该字符串为原串长度为 k k k的前缀。 对于一个字符串 t t t,若某字符串的至少一个子序列为 t t t。则称它是一个 “ “ “含 t t t序列串 ” ” ” 牛牛想要知道对于给定的t,他想要知道s的一个最短前缀满足它是一个 “ “ “含 t t t序列串 ” ” ”,它的长度有多长? 由于答案可能非常大,所以他要求你输出答案对 998244353 998244353 998244353取余数后的结果即可。 特别的,如果 S S S串不存在任何一个前缀满足他是一个 “ “ “含 t t t序列串 ” ” ”,请输出 “ − 1 ” “-1” “−1”表示无解。 t t t串中除了 26 26 26个英文字母以外还会出现 “ ∗ ” “*” “∗”,表示一个通配符。统配符可以视为任意字母。 例如循环 s s s串为 “ a b c a b c a b c a b c . . . " “abcabcabcabc..." “abcabcabcabc...", t t t串为 “ a ∗ c a ” “a*ca” “a∗ca”时,最短含 t t t序列前缀长 4 4 4。而当 t t t串为 “ a ∗ ∗ c a ” “a**ca” “a∗∗ca”时,最短含t序列前缀长 7 7 7。 除此之外,牛牛输入的 t t t串还可能非常非常长,最长可以达到 1 0 1 0 5 10^{10^5} 10105这么长。 所以他想了一种压缩方法,来快速读入 t t t串。 具体来说,它输入的 t t t串中除了 “ ∗ ” “*” “∗”和 26 26 26个小写英文字母以外,还会跟有一些正整数。 在读入字符串时,这些数字表示它前面字母或者"*"重复的次数。 例如 a 5 b c ∗ 3 a5bc*3 a5bc∗3,表示 “ a a a a a b c ∗ ∗ ∗ ” “aaaaabc***” “aaaaabc∗∗∗”。输入的正整数不含前导0。
第一行输入一个仅包含 26 26 26个小写英文字母的字符串 s s s 第二行输入一个正整数 n n n表示, t t t串的数目。 接下来输入 n n n行 再输入一行一个字符串 t t t,表示压缩后的查询串。查询串仅包含 26 26 26个小写英文字母,星号 ′ ∗ ′ '*' ′∗′,以及数字。
对于每一个查询,如果至少存在一个 s s s的前缀满足 “ “ “最短含 t t t序列串 ” ” ”的定义,请输出 s s s的最短含 t t t序列前缀的长度对 998244353 998244353 998244353取余数后的结果。 否则请输出 “ − 1 ” “-1” “−1”表示无解。
本题采用一种叫 “ “ “序列自动机 ” ” ”的玄学操作进行初始化:
for(i=1;i<=n;i++) { for(j=0;j<27;j++) s[j][i]=s[j][i-1]; ++s[T[i]-'a'+1][i],++s[0][i]; }其中 j j j表示字符( 0 0 0表示 ∗ * ∗), i i i表示当前序列元素的下标, s [ j ] [ i ] s[j][i] s[j][i]表示在第 i i i位之前出现了 s [ j ] [ i ] s[j][i] s[j][i]个 j j j 这样初始化完了之后,我们模拟一下,就快乐AC!
#include <bits/stdc++.h> using namespace std; const int N=100012,mod=998244353; int n,m,i,j,l,r,q,v,rem,now,x,res,ne,s[27][N],ans=0;char T[N];bool fl; int ABS(int x){if(x>=mod) return x-mod; if(x<0) return x+mod; return x;} int main() { scanf("%s %d",T+1,&q),n=strlen(T+1); for(i=1;i<=n;i++) { for(j=0;j<27;j++) s[j][i]=s[j][i-1]; ++s[T[i]-'a'+1][i],++s[0][i]; }//序列自动机 while(q--) { scanf("%s",T+1),m=strlen(T+1),ans=0,now=1,fl=1; for(i=1;i<=m;i=r+1) { v=(T[i]=='*') ? 0:(T[i]-'a'+1); if(!s[v][n]){fl=0;break;}r=i,rem=1,res=0; if(i+1<=m&&T[i+1]>='0'&&T[i+1]<='9') { rem=0,l=i+1; while(r+1<=m&&T[r+1]>='0'&&T[r+1]<='9') ++r; x=s[v][n]; for(i=l;i<=r;i++) rem=rem*10+(T[i]-'0'),res=(10ll*res+rem/x)%mod,rem%=x;//大数除法 res=1ll*res*n%mod; if(!rem) rem=x,res=ABS(res-n); }//处理数字并顺带取模 if(s[v][n]-s[v][now-1]<rem) res=ABS(res+n-now+1),rem-=s[v][n]-s[v][now-1],now=1; ne=lower_bound(s[v]+now,s[v]+n+1,s[v][now-1]+rem)-s[v]+1; res=ABS(res+ne-now),now=ne,ans=ABS(ans+res); } if(fl) printf("%d\n",ans); else puts("-1"); } }----原创文章,仅供参考