蓝桥杯[第十一届][B组]-子串分值和
2022-03-31 23:04 幻霞 阅读(211) 评论(0) 收藏 举报
题目来自蓝桥杯练习系统
这一题的样例中明显是不考虑去重的,所以不用去考虑子串的去重,首先可能会想到基本的思路先去转化一下字符串:建立数组a每一位初始化为-1,同时建立记录表数组f,把每一位字符转换成该字符上一次出现的位置,如果第一次出现就不变。然后dp[i][j]得到每一个子串的值(范围是i到j),公式是 dp[i][j]=dp[i][j-1]+(a[j]<i),在上一次的基础上如果该位置字符出现位置在字符串开始位置之前(a[j]<i)值就为false,值为0,否则为true(1),考虑到n范围很大dp数组开不出来,可以用map替代。不过这样写会超时,毕竟字符串可能会很长,这个方法的时间复杂度为O(n^2)
#include <bits/stdc++.h> using namespace std; int a[100005]={0}; // 记录每个字符最后出现的位置 int f[256]; map<pair<int,int>,int> dp; int ans=0; int main() { string s; cin>>s; // 如果选用0作为有意义的数,那么一定要将初始值初始化成别的 memset(f,-1,sizeof(f)); for(int i=0;i<s.size();i++){ if(f[s[i]]==-1){ a[i]=-1; }else{ a[i]=f[s[i]]; } f[s[i]]=i; } for(int l=0;l<s.size();l++){ // 防止循环第一个调用逻辑错误 dp[{l,l-1}]=0; for(int r=l;r<s.size();r++){ dp[{l,r}]=dp[{l,r-1}]+(a[r]<l); ans+=dp[{l,r}]; } } cout<<ans; return 0; }
所以去采用一种更快速的解法:
现在有字符串ababc,要计算每一位字符对所在子串的贡献值,首先每一位字符出现的子串数量为:它以及它前面的字符数量乘它以及它后面的字符数量(意义其实是从该字符开始向前后延伸,前面每延伸i个对应后面延伸j个,比如中间的a,前面不延伸有a,ab,abc三种,前面延伸一个也有ba,bab,babc三种,推广来说就是s.len(0,i)*s.len(i,n-1))
如果字符串全都不同那么就这样每一个字符的贡献值累加到sum就可以了,但是由于前面可能出现相同字符会导致该子串贡献值不为其所在子串数量,即前面的相同字符已经贡献过了,前面含有相同子符的后续串可以不用计算在内。
a,出现在 a,ab,aba,abab,ababc中,对每一个串贡献值为1,和为5,
b,出现在b,ba,bab,babc+ab,aba,abac,ababc,对每一个串贡献值为1,和为8
a,出现在a,ab,abc + ba,bab,babc + aba,abab,ababc,有重复子串 aba,abab,ababc(这几个其实第一个a已经包含过它们了,所以只需要从a算到b a,前面的可以删去不用,因为之前的情况前面的a已经贡献过了),最后算得贡献值为6,
b,出现在了b,bc + ab,abc +bab,babc + abab,ababc,含有相同字符的前串bab ,babc,abab,ababc可以去掉,最后算得贡献值为4,
c,出现在c,bc,abc,babc,ababc对每一个串贡献值为1,和为5
附上代码:
#include <iostream> #include <string.h> using namespace std; typedef long long ll; int f[256]= {0}; string s; int main() { cin>>s; ll ans=0; memset(f,-1,sizeof(f)); for(int i=0; i<s.length(); i++) { // 把不重复的贡献值累加到结果 ans+=(i-f[s[i]])*(s.length()-i); // 更新位置 f[s[i]]=i; } cout<<ans<<endl; return 0; }
把标记数组默认值改为-1,当i=0时即第一个字符时必然前面字符数(包含自己)为1,有0-(-1)=1,如果i=1,1-(-1)=2,若为aa。则1-0=1 。可以理解为没有出现就包含上,出现过了就不包含并且减去上一个出现的位置。(按照索引i一开始为0但是代表了一个字符) 如果默认为0,那就等价于所有出现的字符前一个位置为索引0了,会导致错误结果。当然也可以让s=‘x’+s;来让索引按照序号排列来更好的运算
这题的数据按照最坏情况,即(abcdef......xyzabcd......)虽然子串最大值为26,但是有100000*(100000-1)/2 个子串,数据大了最后必然越界,所以用long long 型变量去累加。