2022-07-21:给定一个字符串str,和一个正数k, 你可以随意的划分str成多个子串

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代码

本作品采用《CC 协议》,转载必须注明作者和本文链接
微信公众号:福大大架构师每日一题。最新面试题,涉及golang,rust,mysql,redis,云原生,算法,分布式,网络,操作系统。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
464
粉丝
21
喜欢
37
收藏
22
排名:461
访问:1.9 万
私信
所有博文
社区赞助商