洛谷P8715 子串分值

题目:

对于一个字符串 S, 我们定义 S 的分值 f(S) 为 S 中恰好出现一次的字符个数。例如 (′′aba′′)=1,(′′abc′′)=3,(′′aaaa′′)=0。现在给定一个字符串S[0..n−1](长度为 n),请你计算对于所有 S 的非空 子串 S[i..j](0≤i≤j<n),f(S[i..j]) 的和是多少。

方法一(50分)

题目要求的是累加母串的每一个子串中不重复的字符个数

STEP ONE:

那么自然先想到要枚举子串

有两种枚举的方法(时间复杂度都是O(n^2) )

枚举长度+左端点--》右端点

for(int len=2;len<=leng;len++)
	for(int l=0;l+len-1<leng;l++)
		int r=l+len-1;

枚举左端点+右端点--》长度

for(int i=0;i<leng;i++)
	{.....
	 for(int j=i+1;j<leng;j++)
		{...}
    }

STEP TWO

计算累加

以第二种枚举方式为例:

第一重循环在于把以确定是枚举第i个元素为首的子序列,第二重循环是进行以第i个元素为首的子序列的枚举,这样两重循环可以保证所有子序列都被枚举到,不重不漏

具体实施:1.由于子序列是不断在延长的,所以后面的数量也依赖于之前的数量

                  2. 每新增一个字符,先判断这个字符是否曾经在已有的子序列中出现过(vector存放已有的字母,find判断是否出现过)

                    2.1  如果没有,那么这一个子序列的cnt要比上一个多一个,所以cnt++,并累加到ans中,把  这个字符存进去

                   2.2 如果出现过:则要考虑是第一次还是第n(n>=2)次,一次并且前一个的cnt大于0,那么就把cnt--,多次的话则不必操作cnt,直接存入vector就可以了(count函数)

if(find(now[i].begin(),now[i].end(),str[j])==now[i].end())
if(count(now[i].begin(),now[i].end(),str[j])==1)

完整代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>

using namespace std;
const int N=1e5+10;
bool vis[N];
char str[N];
vector<char>now[N];
long long ans;
int main()
{
	cin>>str;
	int leng=strlen(str);
	
	
	for(int i=0;i<leng;i++)
	{
		long long  cnt=1;
		now[i].push_back(str[i]);
		ans+=cnt;
		for(int j=i+1;j<leng;j++)
		{
			
			if(find(now[i].begin(),now[i].end(),str[j])==now[i].end())
			{
				cnt++;
				ans+=cnt;
				now[i].push_back(str[j]);
			}
			else
			{
				if(cnt>0&&count(now[i].begin(),now[i].end(),str[j])==1)cnt--;
				now[i].push_back(str[j]);
				ans+=cnt;
			}
		}
	}
	cout<<ans;
	
}

方法二(100分)

step one:对于每一个字母i,找出它是唯一的字符串范围,也就是找出在它之前的出现相同字符的位置,记为pre[i],在他之后出现的相同字符的位置,记为ne[i]

step two:根据组合数的乘法原理,在这段字符串中包含字母i的子串数目为(i-pre[i])*(ne[i]-i)

(以上可以各用一次循环,从后到前,从前到后)

step three:循环每一个字母,累加

拿样例举例

 代码展示:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>


using namespace std;
const int N=1e6+10;
char str[N];//输入str+1,方便pre初始化

int pre[N];//用来存放在这个字母之前相同字母出现的位置,初始化为0
int idx[N];//为了下次找pre做准备
int ne[N];//用来存放在这个字母之后相同字母出现的位置,初始化为len+1
int iidx[N];//为了找ne方便
long long int ans;
int main()
{
	cin>>str+1;
	int len=strlen(str+1);//len还是实实在在的长度,所以下面循环到=len

	for(int i=1;i<=len;i++)//求pre,顺便初始化iidx
	{
		pre[i]=idx[str[i]];
		idx[str[i]]=i;
		iidx[str[i]]=len+1;
	}
	
	for(int i=len;i>=1;i--)//求ne
	{
		ne[i]=iidx[str[i]];
		iidx[str[i]]=i;
	}
	for(int i=1;i<=len;i++)
	{
		ans+=(i-pre[i])*(ne[i]-i);
	}
	cout<<ans;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值