答题卡
作者:互联网
答题卡
题目大意
给定 \(n\) 个字符串,问有多少个二元组 \((i,j)\) 满足 字符串 \(i\) 的长度小于字符串 \(j\) 的长度,且字符串 \(j\) 能够通过下述两种操作变为字符串 \(i\) :
-
删除第一个字母。
-
删除第二个字母。
分析
首先对于这道题有一个非常显然的性质,一个成立的二元组必然满足一下条件:
-
字符串 \(i\) 去掉首字母后的子串能够在字符串 \(j\) 的末端找到。
-
字符串 \(i\) 的首字母能够在 \(j\) 与 \(i\) 相同的末端子串前找到。
之后可以考虑将所有的串全部倒序丢到一棵 \(trie\) 树上,预处理记录两个信息,一个是拥有这个前缀的字符串的数量,而是在这个前缀后有字母 \(x\) 的字符串数量。
之后进行匹配即可。
code
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10,M=1e5+10;
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;
}
int T,n,m;
int nex[M],pre_mode[M];
int pre[N];
char s[N],mode[M];
int last[256];
inline bool equal(int x,int y,int nowlen) { return x==y||x>nowlen&&y>nowlen; }
inline void work()
{
scanf("%s%s",s+1,mode+1);
n=strlen(s+1),m=strlen(mode+1);
memset(last,0,sizeof(last));
for(register int i=1;i<=m;i++){
if(mode[i]>'Z') pre_mode[i]=i-last[mode[i]],last[mode[i]]=i;
else pre_mode[i]=-mode[i];
}
memset(last,0,sizeof(last));
for(register int i=1;i<=n;i++){
if(s[i]>'Z') pre[i]=i-last[s[i]],last[s[i]]=i;
else pre[i]=-s[i];
}
int j=nex[0]=nex[1]=0;
for(register int i=2;i<=m;i++){
while(j&&!equal(pre_mode[j+1],pre_mode[i],j)) j=nex[j];
if(equal(pre_mode[j+1],pre_mode[i],j)) j++;
nex[i]=j;
}
j=0;
int ans=0;
for(register int i=1;i<=n;i++){
while(j&&!equal(pre_mode[j+1],pre[i],j)) j=nex[j];
if(equal(pre_mode[j+1],pre[i],j)) j++;
if(j==m) ans++,j=nex[j];
}
printf("%d\n",ans);
}
int main(){
T=read();
while (T--) work();
return 0;
}
标签:pre,ch,last,int,答题卡,mode,字符串 来源: https://www.cnblogs.com/Defoliation-ldlh/p/15332678.html