2022-07-21:给定一个字符串str,和一个正数k, 你可以随意的划分str成多个子串, 目的是找到在某一种划分方案中,有尽可能多的回文子串,长度>=k,并且没有重合。 返回有几个回文子串。 来

Posted 福大大架构师每日一题

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022-07-21:给定一个字符串str,和一个正数k, 你可以随意的划分str成多个子串, 目的是找到在某一种划分方案中,有尽可能多的回文子串,长度>=k,并且没有重合。 返回有几个回文子串。 来相关的知识,希望对你有一定的参考价值。

2022-07-21:给定一个字符串str,和一个正数k,
你可以随意的划分str成多个子串,
目的是找到在某一种划分方案中,有尽可能多的回文子串,长度>=k,并且没有重合。
返回有几个回文子串。
来自optiver。

答案2022-07-21:

马拉车算法+贪心。

代码用rust编写。代码如下:

use rand::Rng;
fn main() 
    let n: i32 = 20;
    let r = 3;
    let test_time: i32 = 50000;
    println!("测试开始");
    for i in 0..test_time 
        let str = random_string(n, r);
        let k = rand::thread_rng().gen_range(0, str.len() as i32) + 1;
        let ans1 = max1(&str, k);
        let ans2 = max2(&str, k);
        if ans1 != ans2 
            println!("i = ", i);
            println!("str = ", str);
            println!("k = ", k);
            println!("ans1 = ", ans1);
            println!("ans2 = ", ans2);
            println!("出错了!");
            break;
        
    
    println!("测试结束");

// 暴力尝试
// 为了测试
// 可以改成动态规划,但不是最优解
fn max1(s: &str, k: i32) -> i32 
    if s.len() == 0 
        return 0;
    
    let mut str = s.as_bytes().to_vec();
    return process1(&mut str, 0, k);


fn process1(str: &mut Vec<u8>, index: i32, k: i32) -> i32 
    if str.len() as i32 - index < k 
        return 0;
    
    let mut ans = process1(str, index + 1, k);
    for i in index + k - 1..str.len() as i32 
        if is_palindrome(str, index, i) 
            ans = get_max(ans, 1 + process1(str, i + 1, k));
        
    
    return ans;


fn is_palindrome(str: &mut Vec<u8>, mut ll: i32, mut rr: i32) -> bool 
    while ll < rr 
        if str[ll as usize] != str[rr as usize] 
            return false;
        
        ll += 1;
        rr -= 1;
    
    return true;


fn get_max<T: Clone + Copy + std::cmp::PartialOrd>(a: T, b: T) -> T 
    if a > b 
        a
     else 
        b
    


fn get_min<T: Clone + Copy + std::cmp::PartialOrd>(a: T, b: T) -> T 
    if a < b 
        a
     else 
        b
    


// 最优解
// 时间复杂度O(N)
fn max2(s: &str, k: i32) -> i32 
    if s.len() == 0 
        return 0;
    
    let mut str = manacher_string(s);
    let mut p = vec![];
    for _ in 0..str.len() as i32 
        p.push(0);
    
    let mut ans = 0;
    let mut next = 0;
    // k == 5   回文串长度要 >= 5
    // next == 0
    // 0.... 8  第一块!
    // next -> 9
    // 9.....17 第二块!
    // next -> 18
    // 18....23 第三块
    // next一直到最后!
    next = manacher_find(&mut str, &mut p, next, k);
    while next != -1 
        next = if str[next as usize] == ('#' as u8) 
            next
         else 
            next + 1
        ;
        ans += 1;
        next = manacher_find(&mut str, &mut p, next, k);
    
    return ans;


fn manacher_string(s: &str) -> Vec<u8> 
    let str = s.as_bytes().to_vec();
    let mut ans: Vec<u8> = vec![];
    for _ in 0..str.len() as i32 * 2 + 1 
        ans.push(0);
    
    let mut index: i32 = 0;
    for i in 0..ans.len() as i32 
        if (i & 1) == 0 
            ans[i as usize] = '#' as u8;
         else 
            ans[i as usize] = str[index as usize];
            index += 1;
        
    
    return ans;


// s[l...]字符串只在这个范围上,且s[l]一定是'#'
// 从下标l开始,之前都不算,一旦有某个中心回文半径>k,马上返回右边界
fn manacher_find(s: &mut Vec<u8>, p: &mut Vec<i32>, l: i32, k: i32) -> i32 
    let mut c = l - 1;
    let mut r = l - 1;
    let n = s.len() as i32;
    for i in l..s.len() as i32 
        p[i as usize] = if r > i 
            get_min(p[(2 * c - i) as usize], r - i)
         else 
            1
        ;
        while i + p[i as usize] < n
            && i - p[i as usize] > l - 1
            && s[(i + p[i as usize]) as usize] == s[(i - p[i as usize]) as usize]
        
            p[i as usize] += 1;
            if p[i as usize] > k 
                return i + k;
            
        
        if i + p[i as usize] > r 
            r = i + p[i as usize];
            c = i;
        
    
    return -1;


// 为了测试
fn random_string(n: i32, r: i32) -> String 
    let mut ans: String = String::from("");
    let ans_len = rand::thread_rng().gen_range(1, n);
    for _ in 0..ans_len 
        ans.push((rand::thread_rng().gen_range(0, r) + 'a' as i32) as u8 as char);
    
    return ans;


执行结果如下:


左神java代码

以上是关于2022-07-21:给定一个字符串str,和一个正数k, 你可以随意的划分str成多个子串, 目的是找到在某一种划分方案中,有尽可能多的回文子串,长度>=k,并且没有重合。 返回有几个回文子串。 来的主要内容,如果未能解决你的问题,请参考以下文章

2021-12-27:给定一个字符串str,和一个正数k, str子序列的字符种数必须是k种,返回有多少子序列满足这个条件。 已知str中都是小写字母, 原始是取mod, 本节在尝试上,最难的, 搞出

2021-08-30:给定两个字符串str1和str2,在str1中寻找一个最短子串,能包含str2的所有字符,字符顺序无所谓,str1的这个最短子串也可以包含多余的字符。返回这个最短包含子串。(代码

2021-12-02:给定一个字符串str,和一个正数k。 返回长度为k的所有子序列中,字典序最大的子序列。 来自腾讯。

2022-04-07:给定一个只由‘a‘和‘b‘组成的字符串str, str中“ab“和“ba“子串都可以消除, 消除之后剩下字符会重新靠在一起,继续出现可以消除的子串... 你的任务是决定一种消除的

2023-01-06:给定一个只由小写字母组成的字符串str,长度为N, 给定一个只由01组成的数组arr,长度为N, arr[i] == 0表示str中i位置的字符不许修改, arr[i] ==

2021-08-15:给定一个字符串Str,返回Str的所有子序列中有多少不同的字面值。