加密(模拟)
【问题描述】
有一种不讲道理的加密方法是: 在字符串的任意位置随机插入字符。 相应的,
不讲道理的解密方法就是从字符串中恰好删去随机插入的那些字符。
给定原文?和加密后的字符串?,求?有多少子串可以通过解密得到原文?。
【输入格式】
输入第一行包含一个字符串?,第二行包含一个字符串?。
【输出格式】
输出一行,包含一个整数,代表可以通过解密得到原文的?的子串的数量。
【样例输入】
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中所有能成为s的子串
可以换一种说法
我们要找的就是t的包含有s串的元素的子串
但是这个子串只是包含s串还不行
需要包含的s串的元素有顺序
就是说从t[0]到t[length_t-1]中
只要是t[now]和s[0]相等就开始向后面找是否以t[now]开头的子串包含s串的所有元素
如果找到了
则标记以t[now]开头的子串的末尾元素记为t[end]
然后计算t[now]以前的元素个数和t[end]以后的元素个数
这两个个数的值相乘就是t[now]到t[end]这个子串能够延伸的子串个数(包括它自己)
O(lenth_t)循环一遍找到所有的t[now]==s[0]的now值
每个now值都找所有的s串在t串中的顺序值
如果某个now值没有找全s串的值则返回
所有能得出值得now的和
还不是最终的答案
我们会发现这样所有的now值得出的子串个数会有重复
这时需要用容斥原理来减去重复的部分
这样就能得出最后的答案
但是
还没完
虽然答案对了
但是时间复杂度依然高
会超时
所以我们需要做一下优化
就是弄一个数组来储存所有上一个now的s串元素在t串中的位置
如果现在的now的位置<s[1]的位置
则无需再次寻找当前now的s串的元素在t串中位置
我们可以把上一个now的s串的元素的位置引过来用
这样减少寻找now的次数
时间会得到很大的优化
在时间很少的情况下得出答案;
来,上代码:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int len_t,len_s,now_s,be_t=-1,s_where[202]; char t_problem[300005],s_before[202]; unsigned long long int ans=0; bool ok_all=false; void work_for_ans(int now_t) { if(now_t<s_where[1]) { ans+=(now_t-be_t)*(len_t-s_where[len_s-1]); be_t=now_t; } else { now_s=1; for(int i=now_t+1;i<=len_t;i++) { if(i==len_t) { ok_all=true; break; } if(t_problem[i]==s_before[now_s]) { s_where[now_s]=i; now_s++; } if(now_s==len_s) break; } if(!ok_all) { ans+=(now_t-be_t)*(len_t-s_where[len_s-1]); be_t=now_t; } } } int main() { scanf("%s",t_problem); scanf("%s",s_before); len_t=strlen(t_problem),len_s=strlen(s_before); if(len_s==1) { for(int i=0 ; i<len_t ;i++) { if(t_problem[i]==s_before[0]) { ans+=(i-be_t)*(len_t-i); be_t=i; } } cout<<ans<<endl; return 0; } for(int i=0 ; i<len_t ; i++) { if(t_problem[i]==s_before[0]) work_for_ans(i); if(ok_all) break; } cout<<ans<<endl; return 0; }