蓝桥杯刷题——字串分值

322 阅读2分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。

这是20年蓝桥杯省赛的一道题,具体归类到哪一种算法我也不太好说,这更像是一种找规律的题目,蓝桥杯是经常出这类题目的。

题目

给定一个字符串s,f(s)等于s中恰好出现一次字符的个数,例如f(abc)= 3,f(aa)=0,f(ab)=2。 现在给一个字符串,要求求出这个字符串中所有子串经过f()得出的值的全部和。最后输出。这里是大致说明题目意思,要看原题请自行搜索原题。

思路

这里先给出一个字符串ababc,我们围绕这个字符串来解。要求求出所有子串中恰好出现一次字符的个数,我们该如何去做?我们可以先从字符串中的每个字符入手,一个字符出现一次,那么和就可以加1,我们就直接求出这一个字符在所有子串中是否出现,如果在一次子串中出现,我们就可以将和加一。就比如,第一个字符a,他在a,ab,aba,abab,ababc子串中出现,但是在aba,abab,ababc中就不算了,不能加一。所以我们现在的任务是找出出现这个字符的子串但是这个子串中这个字符只能出现一次。如果所有单独字符都能用这样的方法来求,那么这道题我们就做出来了。而实现这个方法也很简单。我们只需找出这个字符上一次出现在哪和下一次出现在哪,例如ababac中的第三个数a,它的上一次出现的位子是1,下一次出现的位子是5。用它(当前的下标减去上一次出现的下标)乘以(下一次出现的下标减去当前下标),这里就是2x2=4,也就是有四个子串只出现一次第三个数a,ba,bab,a,ab。每一个字符都有可以用这种方法得出,题目到此为止也就出来了。

代码

#include<bits/stdc++.h>
using namespace std;
int pre[100010],next[100010],a[27];
string s;
int sum;
int main()
{
	cin>>s;
	s="0"+s;			//下标从1开始 
	int n=s.length()-1;//上面长度加一了,这里要减回去
        memset(a,0,sizeof(a));
	for(int i=1;i<=n;i++)			//寻找前一个相同字符下标 
	{
		int c=s[i]-'a';
		pre[i]=a[c];
		a[c]=i;
	}
	for(int i=0;i<27;i++)			//初始化,可能后边没有相同字符    寻找前一个时,
		a[i]=n+1;
	for(int i=n;i>0;i--)				//寻找后一个相同字符下标 
	{
		int c=s[i]-'a';
		next[i]=a[c];
		a[c]=i;
	}
	
	for(int i=1;i<=n;i++)
		sum+=(long long)(i-pre[i])*(next[i]-i);
	cout<<sum; 
	return 0;
}