密码(子序列)
给你一个加密后的字符串,一个加密前的字符串,问有多少种方法,使得加密后的字符串从两边切割后,包含加密前字符串的子序列。
既然是要求切割方法,肯定要考虑重复情况。我们枚举切割的左端点,然后找到包含未加密字符串的最短子序列的右端点,然后统计答案即可。
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const LL maxn=3e5+5, maxm=205;
LL len1, len2, ans;
LL right[maxn][26];
char s1[maxn], s2[maxm];
void preprocess(){
for (LL j=0; j<26; ++j){
right[len1][j]=-2;
right[len1-1][j]=-2;
}
right[len1-1][s1[len1-1]-'a']=len1-1;
for (LL i=len1-2; i>=0; --i)
for (LL j=0; j<26; ++j)
if (s1[i]-'a'==j) right[i][j]=i;
else right[i][j]=right[i+1][j];
}
int main(){
scanf("%s%s", s1, s2);
len1=strlen(s1); len2=strlen(s2);
preprocess(); LL j, k;
for (LL i=0; i<len1; ++i){
k=i;
for (j=0; j<len2; ++j){
if (!~k) break;
k=right[k][s2[j]-'a']+1;
}
if (~k) ans+=len1-k+1;
}
printf("%lld", ans);
return 0;
}