算法学习记录
主要是记录自己学习的过程,题目来自塔子哥[塔子哥网站](首页 - 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;
}
}