[JZOJ3484]密码

题目大意:
  给你一个很长的字符串a(|a|<=300000),一个比较短的字符串b(|b|<=200),请你搞一些破坏。
  你可以从a的两边去掉一些字符使得b仍是a的一个字串,问有多少种方案?

思路:
  首先预处理一下串a,用f[i][j]记录对于第i个位置的字符,左边最靠近i的字符j在哪里。
  然后枚举每一个字符作为最后一个字符,往前跳,如果把整个b串跳完了,就说明减掉这两边的都没关系,计入方案。
  设a的范围为(1,n),b的范围为(i,j)那么答案增加i*(n-j+1)。
  然而这样会重复算很多,不去重直接爆零了。
  考虑如何去重。
  记录一下上次找到的序列最右边的端点在哪里,记为last,答案增加i*(last-j)。

 1 #include<cstdio>
 2 #include<cstring>
 3 typedef long long int64;
 4 const int N=300002,M=202;
 5 char s[N],t[M];
 6 int f[N][26];
 7 inline int idx(const char &ch) {
 8     return ch-'a';
 9 }
10 int main() {
11     scanf("%s%s",s+1,t+1);
12     int n=strlen(s+1),m=strlen(t+1);
13     for(register int i=1;i<n;i++) {
14         memcpy(f[i+1],f[i],sizeof *f);
15         f[i+1][idx(s[i])]=i;
16     }
17     int64 ans=0;
18     int last=n+1;
19     for(register int i=n;i;i--) {
20         if(s[i]!=t[m]) continue;
21         for(register int j=m,k=i,l;j&&k;k=f[l=k][idx(t[--j])]) {
22             if(j==1) {
23                 ans+=k*(last-i);
24                 last=i;
25             }
26         }
27     }
28     printf("%lld\n",ans);
29     return 0;
30 }

 

posted @ 2017-11-01 11:03  skylee03  阅读(130)  评论(0编辑  收藏  举报