2022-07-21:給定一個字串str,和一個正數k, 你可以隨意的劃分str成多個子串

moonfdd發表於2022-07-21

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,雲原生,演算法,分散式,網路,作業系統。

相關文章