0
点赞
收藏
分享

微信扫一扫

如何在多个端口上运行 SSH 服务器?

佳简诚锄 2023-06-06 阅读 87
c++

关于子串分值,字符串中所有子串中的唯一出现字符个数的和,在力扣有原题

子串分值和

 n有十万,需要找规律,O(n^2)不满足要求

分析样例:

Ababc

01234

长度是n=5

索引下标-对应字符

0A贡献 1+1=2 a;ab;---2=2*1 next a 2; pre a -1

1b贡献 1+1=2 b;ba;---4=2*2 next b 3; pre b -1

2a贡献 1+1+1=3 a;ab;abc; ---9=3*3 next a 没有 5;pre a 0

3b贡献 1+1=2 b;bc;   8=2*4 next b 没有 5;pre b 1

4c贡献 1=1 c----1*5 next c 没有 5;pre c -1

字符位置i+1   *   字符贡献长度  求和

对于i处的字符s[i],

上一个s[i]出现位置p[s[i]]

i-p[s[i]]   贡献次数 n[s[i]]-i

ababc

28

A,ab,aba,abab,ababc;

B,ba,bab,babc;

A,ab,abc;

B,bc;

c

因此,需要记录i处字符a[i]的next和pre数组,初始化pre为-1,next为字符串长度。

next[i]表示s[i]的下一处下标索引

Next[i]记录s[i]的下一处,pre[i]记录s[i]的下一处,

如何记录

遍历s[i]

如何更新next[i],

对于得到的s[i],ii=s[i],next[ii]=i,下一个位置就是当前位置

res+=(next[ii]-i)*(i-pre[ii])// next[ii]=I,,此处加的都是0,错误的

Pre[ii]=I;

Next[ii]未知

如何更新pre[i]

已知一个字符在字符串里出现过三次及以上,如何快速求出字符串中一个字符的前一个出现位置和后一个出现位置

使用三个数存储即可,存储pre,now,next。

ababc

(i+1)(n-i)

58985

58645

a

ab

aba

abab

ababc

 b

 ba

 bab

 babc

  a

  ab

  abc

   b

   bc

    c

0 1*5

1 2*4

2 3*3

3 4*2

4 5*1

如果字符都不重复, (i+1)  *  (n-i)          

如果字符有重复, (i+1)   *   (n-i )         减去 (pre[i]+1) * (n-pre[i])

下标从0开始,对于某个字符串,设字符c出现在位置i处,则该字符在第0、1、...、i轮列举都有该字符的贡献,出现了i+1轮,每轮列举,由于依次列举1个、2个...、n个字符(n-轮数+1个字符),每轮出现第i位置处的字符个数有n-i个(要么不出现,要么是n-1个)。

字符串由起点和终点确定,从乘法原理,不能重复计数,所以,设字符c出现在位置i处,前面出现字符c的位置是pre[i],从pre[i]+1,...,i处都可以作为子串的起点,再枚举终点,从i到n-1都可以作为字串的终点。

代码如下

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

using namespace std;
const int N=100050;
char s[N];
int pre[N];
int tmppre[50];


int main(){
	scanf("%s",s);
	int n;
	n=strlen(s);
	long long sum=0;
	for(int i = 0; i < 26;i++)tmppre[i]=-1;
	for(int i = 0; i < n;i++)
	{
		int ch=s[i]-'a';
		pre[i]=tmppre[ch];//获取ch上一个位置 
		tmppre[ch]=i;//更新ch位置 
		sum+=1ll*(i-pre[i])*(n-i);
	}	
	printf("%lld\n",sum);
	return 0;
}

子串分值

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

using namespace std;
const int N=100050;
char s[N];
int pre[N];
int tmppre[50];
int nxt[N];
int tmpnxt[50];

int main(){
	scanf("%s",s);
	int n;
	n=strlen(s);
	long long sum=0;
	for(int i = 0; i < 26;i++)tmppre[i]=-1,tmpnxt[i]=n;
	for(int i = 0; i < n;i++)
	{
		int ch=s[i]-'a';
		pre[i]=tmppre[ch];//获取ch上一个位置 
		tmppre[ch]=i;//更新ch位置 
	}	
	for(int i = n-1; ~i;i--)
	{
		int ch=s[i]-'a';
		nxt[i]=tmpnxt[ch];//获取ch上一个位置 
		tmpnxt[ch]=i;//更新ch位置 
	}
	for(int i = n-1; ~i;i--)
		sum+=1ll*(i-pre[i])*(nxt[i]-i);
	printf("%lld\n",sum);
	return 0;
}

蓝桥杯:子串分值_都是Bug惹的祸的博客-CSDN博客

这道题用暴力法只能得到一般的分数,运行会超时,所以只能计算每个字母的贡献值,这样可以节约运行时间。
要想计算贡献值,对每一个字母 x[i],找到前一个相同的字母下标 pre[i],后一个相同字母的下标 next[i],然后用贡献值的计算公式
(i-pre[i])*(next[i]-i) (这个公式计算的是一个字母的贡献值,将所有的字母的贡献值加在一起就是结果了)

​ 这个算法是计算字符串中每个字符的贡献值即影响因子,将贡献值累加就得到了结果。那么该如何来计算每个字符的贡献值呢?
所谓贡献值,即该字符能够影响到的子串个数,就比如
​ 0 1 2 3 4 5
​ b a c b e b
表格中的3号b能够影响多少个子串呢?
列举一下有:acbe,acb,cbe,cb,b,be共有6个
(这些子串的f值都因为b增加了1,而 bacb这个子串不会因为3号b增加f值).
那么如何用数学的方法计算出来贡献值呢?
这些子串的起点可以为1、2、3,终点可以为3、4,即(3-0)(5-3) = 6
其中 0 是 3号 b往左遇到的第一个b的标号,而 5是 3号 b往右遇到的第一个 b的标号。
!!!!!推论:一个字符(位置为i)的影响范围由其往左边遍历遇到的第一个相同字符(位置为left)和
​ 其往右边遍历遇到的第一个相同字符(位置为right)所决定。其贡献值为(i - left)*(right - i)
乘法原理 O ( N ):

解题思路:

1、统计每个字母在仅出现一次的情况下,能被多少子串所包含;
2、用 pre[i] 记录第 i 个字母上一次出现的位置,用 next[i] 记录第 i 个字母下一次出现的位置;
3、那么往左最多能延伸到 pre[i] + 1,其到第 i 个字母一共有 i - pre[i] 个字母,作为子字符串起点;
4、同理往右最多能延伸到 next[i] - 1,其到第 i 个字母一共有 next[i] - i 个字母,作为子字符串终点;
5、二者相乘,就是该字母被不同子串所包含的总次数;

字符贡献度问题_字符串贡献度_白龙码~的博客-CSDN博客

举报

相关推荐

0 条评论