加密

【问题描述】
有一种不讲道理的加密方法是: 在字符串的任意位置随机插入字符。 相应的,
不讲道理的解密方法就是从字符串中恰好删去随机插入的那些字符。
给定原文?和加密后的字符串?,求?有多少子串可以通过解密得到原文?。
【输入格式】
输入第一行包含一个字符串?,第二行包含一个字符串?。
【输出格式】
输出一行,包含一个整数,代表可以通过解密得到原文的?的子串的数量。
【样例输入】
abcabcabc
cba
【样例输出】
9
【样例解释】
用[?,?]表示子串开头结尾的下标(从 0 开始编号) ,这 9 种方案是:
[0,6],[0,7],[0,8],[1,6],[1,7],[1,8],[2,6],[2,7],[2,8]
【数据规模和约定】
30%的数据,|?| 1000。
对于100%的数据,1 ≤ |?| ≤ 300,000,1 ≤ ? ≤ 200。

/*
  这道题想出了怎么做,然而写挂了~
  预处理出t数组中每个元素对应的与它邻近的a-z分别在哪儿,然后直接模拟可做。
  一开始我预处理的与它邻近的并且与它相同的元素距离是0,所以后来模拟的时候碰到s数组类似于“abbc”这种有连续相同字符的就挂了。
  本来自己造了几组数据,和暴力的结果都一样,然而没有造到这种数据,以后对拍一定要写对拍程序!!!
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#define M 300010
#define N 1010
#define lon long long
using namespace std;
char t[M],s[N];
int near[M][30],m,n,head[M],tail[M],num;
void work(){
    int p=-1;lon tot=0;
    for(int i=0;i<m;i++){
        if(t[i]!=s[0])continue;
        tot+=(lon)(i-p)*(lon)(m-i);
        p=i;
    }
    cout<<tot;
}
int main(){
    freopen("encrypt.in","r",stdin);
    freopen("encrypt.out","w",stdout);
    cin>>t>>s;
    m=strlen(t);n=strlen(s);
    if(n==1){
        work();return 0;
    }
    for(int i=1;i<=26;i++) near[m][i]=M;
    for(int i=m-1;i>=0;i--){
        for(int j=1;j<=26;j++){
            if(t[i]==j+'a'-1) near[i][j]=0;
            else if(near[i+1][j]==M) near[i][j]=M;
            else near[i][j]=near[i+1][j]+1;
        }
    }
    for(int i=0;i<=m;i++){
        for(int j=1;j<=26;j++){
            if(!near[i][j])
                near[i][j]=near[i+1][j]+1;
        }
    }
    for(int i=0;i<m;i++){
        if(t[i]!=s[0])continue;
        int j=i,sum=1;
        while(1){
            j+=near[j][s[sum]-'a'+1];
            sum++;
            if(j>=M||sum==n)break;
        }
        if(sum==n&&j<M-10){
            head[++num]=i;tail[num]=j;
        }
    }
    lon tot=0;head[0]=-1;
    for(int i=1;i<=num;i++){
        tot+=(lon)(head[i]-head[i-1])*(lon)(m-tail[i]);
    }
    cout<<tot;
    return 0;
}
原文地址:https://www.cnblogs.com/harden/p/6057042.html