蓝桥杯2020年真题演练——8、字串分值(JavaA组)

本文介绍了两种方法解决蓝桥杯2020年真题——回文日期。方法一采用暴力枚举,虽然时间复杂度高但思路直观;方法二通过计算每个字母对分值的贡献度,降低了复杂度。文章提供了详细代码实现,并分析了两种方法的优劣。
摘要由CSDN通过智能技术生成

上一题:蓝桥杯2020年真题演练——7、回文日期(JavaA组)

题目描述

在这里插入图片描述
在这里插入图片描述

解析

方法一:暴力枚举

⭐⭐对于这道题,最直观的想法就是双重循环枚举所有字串,然后依次计算字串的f(“字串”),而f的计算也不困难,创建一个数组统计字符串中各字母出现的次数,再统计次数为1的字母的个数即是f的值。这种方法思路简单,容易实现,但有一个致命缺陷,时间复杂度太大!,根本无法通过所有的评测用例,甚至一半都通不过,不过没有别的办法的话,这种方法也能得一些分。

代码实现

  1. 双重循环枚举字串,这里要注意我们不需要真的new一个空间存储字串,只需要记录原字符串的下标i,j,然后在i到j范围内进行计算f即可。(内层循环j=i,避免忽略字串为单个字母的情况)
  2. 对每一个枚举的实例,我们实际获得的是下标i,j,所以我们是通过i,j,s(原字符串)这三个参数来计算f的。
  3. 然后就是计算f了,这一步代码中有很详细的注释,直接看代码吧
public class Main {
	public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String s = scan.nextLine();
        int totalFractions = 0;//总分
        for(int i=0;i<s.length();i++) {
        	for(int j=i;j<s.length();j++) {
        		totalFractions+=f(i,j,s);
        	}
        }
        System.out.println(totalFractions);
        scan.close();
	}
	public static int f(int i,int j,String s) {
		int fractions=0;//记录只出现一次的字母的个数
		int[] a=new int[26];//26个字母预留26个位置
		for(int k=i;k<=j;k++) {
			int ii=s.charAt(k)-'a'; //char和char相减得到int(实际上是ASCII码相减)
			a[ii]++;//对应字母加一
			
		}
		//遍历a中的每一个元素
		for(int ch: a) {
			if(ch==1) {
				fractions++;
			}
		}
		return fractions;
	}
}

方法二:直接计算每个字母对分值的贡献度

⭐⭐我们可以不求字串,而是直接求原字符串中每一个字母,能在哪些字串中得分,这样把每一个字母的得分都算出来后相加,就得到最终结果了。
🌙举例:“ababc”中的第一个b,它分别在字串ab,aba,b,ba四个字串中得分,所以它的贡献为4
再看第二个a,它分别在字串ba,bab,babc,a,ab,abc六个字串中得分,所以它的贡献度是6。
分析一下,某个字母究竟在哪些字串中得分?这个还是比较容易发现的,比如第二个a,在上一次a出现之后和下一次a出现之前,这之间的字母组成的字符串中只出现一次a,即:babc,a后边俩个字母,前边一个字母,那贡献度就是(1+1)✖(2+1)=6(计算babc的包含a的字串数目)。这个方法不如前边的好理解但复杂度较低,可以通过所有的测评用例。

代码实现

  1. 首先我们需要计算出每个位置上的字母上一次出现的位置和下一次出现的位置,如果没有上一个,那“上一个”位置就设为0,如果没有下一个,那“下一个”位置就设置为s.length()
    另外还需要俩个数组pre 和suf存储位置信息

  2. 用last存储对应字母出现的位置,当某个字母再次出现时,就会把上次存储的字母的位置传给pre或suf

  3. 最后由(i-pre[i])*(suf[i]-i)计算贡献度,然后相加得到答案

public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String s = scan.nextLine();
        int[] pre = new int[s.length() + 1];
        int[] suf = new int[s.length() + 1];
        int[] last = new int[30];
        //默认字母前一次出现的位置是-1(即不存在前一次的时候就是-1)
        Arrays.fill(last, -1);
        for (int i = 0; i < s.length(); i++) {
            int x = s.charAt(i) - 'a';//x是字母对应的序号
            pre[i] = last[x];//再一次出现该字母时就把该字母上一次出现的位置存储起来了
            last[x] = i;//last记录本次该字母出现的位置
        }
        //默认字母后一次出现的位置是s.length()(即不存在后一次的时候就是s.length())
        Arrays.fill(last, s.length());
        for (int i = s.length()-1; i >=0 ; i--) {
            int x=s.charAt(i)-'a';//x是字母对应的序号
            suf[i]=last[x];
            last[x]=i;
        }
        //计算每一个位上的字母的贡献度
        int ans=0;
        for (int i = 0; i < s.length(); i++) {
            ans+=(i-pre[i])*(suf[i]-i);
        }
        System.out.println(ans);
        scan.close();
    }

如果对小伙伴有用的话,希望点个攒支持一下我,非常感谢
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值