算法学习记录

160 阅读3分钟

算法学习记录

主要是记录自己学习的过程,题目来自塔子哥[塔子哥网站](首页 - CodeFun2000)。答案也参考塔子哥中的题解,加上自己的理解。

他的算法是这样的:首先,他将外界的声音转录成一个只包含小写字母的字符串 s ,然后计算这个字符串的权值 w(s) ,定义为 s 的长度乘以 s 中不同字母的个数。例如,w(“abacb”)=5∗3=15 。然后,他将 s 切分成 k 个连续的子串 s1, s2, …, sk ,使得这 k 个子串中权值最大的那个尽可能小。这样,他就可以忽略掉那些权值较大的子串,只关注那些权值较小的子串,从而达到过滤噪声的目的。

你想试试这种算法,于是你输入了一个字符串 s 和一个正整数 k ,表示你想将 s 切分成 k 个子串。你需要输出切分后权值最大的子串的权值。你可以假设 s 的长度不超过 500000 ,且 k 不超过 s 的长度。

import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class Main09211 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String s = scanner.nextLine();
        int k = scanner.nextInt();
        System.out.println(Tencent2023_0323_2(s,k));
    }
    //计算权值的函数
    private static int weight(String s)
    {
        Set<Character> set = new HashSet<>();
        for (char c : s.toCharArray()) {
            set.add(c);
        }
        return set.size() * s.length();
    }
    //二分查找 找尽可能小的最大权值
    /**
     * 本题的意思是将当前字符串分割成k个子串,每一种分割方式都有一个最大权值
     * 本题目的是找出这些分割方式中最大权值中的最小权值
     * 使用二分查找的方式解决这道题
     * @param s
     * @param k
     * @return
     */
    public static int Tencent2023_0323_2(String s,int k){
        int n = s.length();
        int ans = -1;
        // 第一种分割方式就是将字符串分割成n - k + 1这样的字符串,这里就是所有分割方式权值最大的一种分割方式
        // 通过二分查找的方式来找到最小的最大权值
        //下面就是正常的二分逻辑,找权值,实际上核心的过程是check函数
        int maxValue = weight(s.substring(0,n - k + 1));
        int left = 1;
        int right = Math.min(maxValue,900);
        while(left <= right)
        {
            int mid = (left + right) / 2;
            // 表示有一种分割方式,这种分割方式的最大权值满足mid
            // 为了去找最小的权值,自然要在数组的前半部分寻找
            if(check(s,mid,k))
            {
                ans = mid;
                right = mid - 1;
            }
            else
            {
                left = mid + 1;
            }
        }
        return ans;
    }

    /**
     * 这个函数的功能就是将字符串s分割成k个子串
     * 判断分割后的子串的最大权值是否满足maxValue
     * count表示分割的次数
     * @param s
     * @param maxValue
     * @param k
     * @return
     */
    public static boolean check(String s,int maxValue,int k)
    {
        int n = s.length();
        int count = 0;
        // 通过双指针的方式找子串
        int left = 0;
        int right = 0;
        while(right < n)
        {
            int w = weight(s.substring(left,right + 1));
            if (w > maxValue)
            {
                count++;
                left = right;
                right--;
            }
            if (count > k || (count ==k && right != n - 1))
            {
                return false;
            }
            right++;
        }
        return count <= k;
    }

}