【问题描述】
有一种不讲道理的加密方法是: 在字符串的任意位置随机插入字符。 相应的,
不讲道理的解密方法就是从字符串中恰好删去随机插入的那些字符。
给定原文?和加密后的字符串?,求?有多少子串可以通过解密得到原文?。
【输入格式】
输入第一行包含一个字符串?,第二行包含一个字符串?。
【输出格式】
输出一行,包含一个整数,代表可以通过解密得到原文的?的子串的数量。
【样例输入】
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; }