子串分值和(蓝桥杯C组)

2022.4.30新增一题发现类似:子串的最大差(代码源)

题目链接
在这里插入图片描述
首先我们要弄清楚,所有 f [ i . . j ] f[i..j] f[i..j]之和,即对于每个下标的字符包含这个下标的字符的子串之和,还要加一点限制,选择的子串只能有一个这个下标的字符。

(1).当选择一个子串时,如果重复选择了一个字符,那么这个字符的对答案的贡献变为0,否则只有一个这个字符,对答案的贡献为1。

比如 f [ " a b " ] = 2 , 但 f [ " a b a " ] = 1 。 因 为 ‘ a ’ 重 复 选 取 了 f["ab"]=2,但f["aba"]=1。因为‘a’重复选取了 f["ab"]=2,f["aba"]=1a

再来一个例子如样例 s s s=“ a b a b c ababc ababc
i = 0 时 i=0时 i=0,选择包含 s [ 0 ] s[0] s[0]的子串一共有5个串:
{“a”,“ab”,“aba”,“abab”,“ababc”}
第一个串 f = 1 f=1 f=1;
第二个串 f = 2 f=2 f=2;
第三个串 f = 1 f=1 f=1;
第四个串 f = 0 f=0 f=0;
第五个串 f = 1 f=1 f=1;
选择第三个串重复了 ′ a ′ 'a' a字符,导致 i = 0 i=0 i=0 ′ a ′ 'a' a的贡献被抵消了,所以 s [ 0 ] s[0] s[0]到下标为2之后这个字符对答案贡献就为0了

(2).所有 f [ i . . j ] f[i..j] f[i..j]之和,等于对于每个下标的字符包含这个下标的字符的子串之和,还要加一点限制,选择的子串只能有一个这个下标的字符。

例如样例:
i = 0 i=0 i=0,时满足(2)的子串{ “ a ” , " a b " “a”,"ab" a,"ab"}每个子串a的贡献为1 (+ 2)
i = 1 i=1 i=1,时满足(2)的子串 { " b " , " b a " , " a b " , " a b a " "b","ba","ab","aba" "b","ba","ab","aba"}每个子串b的贡献为1 (+4)
i = 2 i=2 i=2,时满足(2)的子串 { " a " , " a b " , " a b c " , " b a " , " b a b " , " b a b c " "a","ab","abc","ba","bab","babc" "a","ab","abc","ba","bab","babc"}每个子串a的贡献为1 (+6)
i = 3 i=3 i=3,时满足(2)的子串 { " a " , " a b " , " a b c " , " b a " , " b a b " , " b a b c " "a","ab","abc","ba","bab","babc" "a","ab","abc","ba","bab","babc"}每个子串b的贡献为1 (+6)
i = 4 i=4 i=4,时满足(2)的子串 { " c " , " b c " , " a b c " , " b a b c " , " a b a b c " "c","bc","abc","babc","ababc" "c","bc","abc","babc","ababc"}每个子串c的贡献为1 (+5)
合起来等于ans=21

所以我们观察子串选择规律得到(我写的有规律),对于每个下标的字符,选择这个字符的左边离相同字符的距离*右边离相同字符的距离,即 a n s + = ( i − p r e [ i ] ) ∗ ( n e [ i ] − i ) ans+=(i-pre[i])*(ne[i]-i) ans+=(ipre[i])(ne[i]i)
具体解释看下方代码注释:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 12;

int pre[N];//pre[i]表示左边和s[i]相同字符最近的下标
int ne[N];//ne[i]表示右边和s[i]相同字符最近的下标
string s;//存入字符串
int t[26];//0-25对应‘a’-'z',这个数组起临时保存这个字符上一次出现的下标

int main()
{
	cin >> s;//读入字符串
	int n = s.size();//得到字符串长度
	/*先求右边的最近的相同字符的下标*/
	for (int i = 0; i < 26; i++)//初始右边没有相同字符,所以置为n
	{
		t[i] = n;
	}
	long long  ans = 0;
	for (int i = n - 1; i >= 0; i--)
	{
		int x = s[i] - 'a';//将'a'-'z'映射为0-25
		ne[i] = t[x];//与i位置相同的右边最近的字符的位置
		t[x] = i;//更新上一个此字符为本身
	}
		/*先求左边的最近的相同字符的下标*/
	for (int i = 0; i < 26; i++)//字符串下标从0开始,所以左边相同字符位置初始置为-1
	{
		t[i] = -1;
	}
	for (int i = 0; i < n; i++)
	{
		int x = s[i] - 'a';//将'a'-'z'映射为0-25
		pre[i] = t[x];//与i位置相同的左边最近的字符的位置
		t[x] = i;//更新上一个此字符为本身
	}

	for (int i = 0; i < n; i++)
	{
		ans += 1LL * (i - pre[i]) * (ne[i] - i);//左边离相同字符的距离*右边离相同字符的距离
	}
	cout << ans << endl;


}

还有一题差不多的建议做做,只有 f f f的定义一点不同
子串分值和B组

点个赞呗~

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值