【動態規劃 前綴和】2478. 完美分割的方案數

本文涉及知識點

劃分型dp 動態規劃匯總
C++算法:前綴和、前綴乘積、前綴異或的原理、源碼及測試用例 包括課程視頻

LeetCode 2478. 完美分割的方案數

給你一個字符串 s ,每個字符是數字 ‘1’ 到 ‘9’ ,再給你兩個整數 k 和 minLength 。
如果對 s 的分割滿足以下條件,那么我們認為它是一個 完美 分割:
s 被分成 k 段互不相交的子字符串。
每個子字符串長度都 至少 為 minLength 。
每個子字符串的第一個字符都是一個 質數 數字,最后一個字符都是一個 非質數 數字。質數數字為 ‘2’ ,‘3’ ,‘5’ 和 ‘7’ ,剩下的都是非質數數字。
請你返回 s 的 完美 分割數目。由于答案可能很大,請返回答案對 109 + 7 取余 后的結果。
一個 子字符串 是字符串中一段連續字符串序列。
示例 1:
輸入:s = “23542185131”, k = 3, minLength = 2
輸出:3
解釋:存在 3 種完美分割方案:
“2354 | 218 | 5131”
“2354 | 21851 | 31”
“2354218 | 51 | 31”
示例 2:
輸入:s = “23542185131”, k = 3, minLength = 3
輸出:1
解釋:存在一種完美分割方案:“2354 | 218 | 5131” 。
示例 3:
輸入:s = “3312958”, k = 3, minLength = 1
輸出:1
解釋:存在一種完美分割方案:“331 | 29 | 58” 。
提示:
1 <= k, minLength <= s.length <= 1000
s 每個字符都為數字 ‘1’ 到 ‘9’ 之一。

劃分型dp

預處理

bool bPrime[128] 記錄那些字符是質數。
vNotPirme記錄所有非質數下標。二維向量lr,記錄所有可以劃分的區間,左閉右開。如果s[i]不是質數,則s[i]為空。
否則將所有r ∈ \in vNotPrime ,且 r- i +1 >= minLength 的r+1放到s[i]中。只后就是經典的劃分性dp。
時間復雜度 : O(n3)超時。

代碼

class Solution {
public:int beautifulPartitions(string s, int K, int minLength) {bool bPrime[128] = { false };bPrime['2'] = bPrime['3'] = bPrime['5'] = bPrime['7']=true;	const int N = s.length();vector<int> notPrime;for (int i = 0; i < N; i++) {if (bPrime[s[i]]) { continue; }notPrime.emplace_back(i);}vector<vector<int>> lr(N);for (int i = 0,j=0; i < N; i++) {if (!bPrime[s[i]]) { continue; }while ((j < notPrime.size()) && (notPrime[j] - i + 1 < minLength)) {j++;}for (int k = j; k < notPrime.size(); k++) {lr[i].emplace_back(notPrime[k] + 1);}}vector<vector<C1097Int<>>> dp(K+1,vector<C1097Int<>>(N + 1));dp[0][0] = 1;for (int i = 0; i < N; i++) {for(int k = 0; k < K ;k++ )for (int r : lr[i]) {dp[k + 1][r] += dp[k][i];}}return dp.back().back().ToInt();}
};

動態規劃+前綴和

如果s[0]不是質數或s.back()是質數,無法分割,直接返回0。
分成k段有k-1分割點。
s[i]非質數,s[i+1]是質數,則可以分割。
vSplit記錄所有的分割點,為了避免處理邊界vSplit = {-1}; 最后追加n-1

動態規劃的狀態

dp[i][k]表示處理完vSplit[i]的分割k段的方案。 空間復雜度:O(mk) m= vSplit.size()

動態規劃的轉移方程

dp[i][k] = ∑ j : 0 v S p l i t [ i ] ? v S p l i t [ j ] > = m i n L e n g h t \Large\sum_{j:0}^{vSplit[i]-vSplit[j] >= minLenght } j:0vSplit[i]?vSplit[j]>=minLenght?dp[j][k-1]
利用前綴和優化,單個狀態轉移的時間復雜度是:O(1)
時間復雜度:O(mk)

動態規劃的填表順序

i從1到m-1,k從1到K

動態規劃的初始值

dp[0][0] = 1 ,其余全為0 .

動態規劃的返回值

dp.back().back()

代碼

核心代碼

template<int MOD = 1000000007>
class C1097Int
{
public:C1097Int(long long llData = 0) :m_iData(llData% MOD){}C1097Int  operator+(const C1097Int& o)const{return C1097Int(((long long)m_iData + o.m_iData) % MOD);}C1097Int& operator+=(const C1097Int& o){m_iData = ((long long)m_iData + o.m_iData) % MOD;return *this;}C1097Int& operator-=(const C1097Int& o){m_iData = (m_iData + MOD - o.m_iData) % MOD;return *this;}C1097Int  operator-(const C1097Int& o){return C1097Int((m_iData + MOD - o.m_iData) % MOD);}C1097Int  operator*(const C1097Int& o)const{return((long long)m_iData * o.m_iData) % MOD;}C1097Int& operator*=(const C1097Int& o){m_iData = ((long long)m_iData * o.m_iData) % MOD;return *this;}C1097Int  operator/(const C1097Int& o)const{return *this * o.PowNegative1();}C1097Int& operator/=(const C1097Int& o){*this /= o.PowNegative1();return *this;}bool operator==(const C1097Int& o)const{return m_iData == o.m_iData;}bool operator<(const C1097Int& o)const{return m_iData < o.m_iData;}C1097Int pow(long long n)const{C1097Int iRet = 1, iCur = *this;while (n){if (n & 1){iRet *= iCur;}iCur *= iCur;n >>= 1;}return iRet;}C1097Int PowNegative1()const{return pow(MOD - 2);}int ToInt()const{return m_iData;}
private:int m_iData = 0;;
};class Solution {
public:int beautifulPartitions(string s, int K, int minLength) {bool bPrime[128] = { false };bPrime['2'] = bPrime['3'] = bPrime['5'] = bPrime['7']=true;	if ((!bPrime[s[0]]) || (bPrime[s.back()])) { return 0; }const int N = s.length();vector<int> vSplit = { -1 };for (int i = 0; i+1 < N; i++) {if (bPrime[s[i]]) { continue; }if (bPrime[s[i + 1]]) { vSplit.emplace_back(i); }}		vSplit.emplace_back(N - 1);const int M = vSplit.size();vector<vector<C1097Int<>>> dp(M , vector<C1097Int<>>(K + 1));dp[0][0] = 1;vector<C1097Int<>> vSum(K);for (int m = 1, i = 0; m < M; m++) {while ((i < M) && (vSplit[m] - vSplit[i] >= minLength)) {for (int k = 0; k < K; k++) {vSum[k] += dp[i][k];}i++;}for (int k = 1; k <= K; k++) {dp[m][k] = vSum[k - 1];}}		return dp.back().back().ToInt();}
};

單元測試

template<class T1, class T2>
void AssertEx(const T1& t1, const T2& t2)
{Assert::AreEqual(t1, t2);
}template<class T>
void AssertEx(const vector<T>& v1, const vector<T>& v2)
{Assert::AreEqual(v1.size(), v2.size());for (int i = 0; i < v1.size(); i++){Assert::AreEqual(v1[i], v2[i]);}
}template<class T>
void AssertV2(vector<vector<T>> vv1, vector<vector<T>> vv2)
{sort(vv1.begin(), vv1.end());sort(vv2.begin(), vv2.end());Assert::AreEqual(vv1.size(), vv2.size());for (int i = 0; i < vv1.size(); i++){AssertEx(vv1[i], vv2[i]);}
}namespace UnitTest
{	string s;int k, minLength;TEST_CLASS(UnitTest){public:TEST_METHOD(TestMethod00){s = "23542185131", k = 3, minLength = 2;auto res = Solution().beautifulPartitions(s, k, minLength);AssertEx(3, res);}TEST_METHOD(TestMethod01){s = "23542185131", k = 3, minLength = 3;auto res = Solution().beautifulPartitions(s, k, minLength);AssertEx(1, res);}TEST_METHOD(TestMethod02){s = "3312958", k = 3, minLength = 1;auto res = Solution().beautifulPartitions(s, k, minLength);AssertEx(1, res);}TEST_METHOD(TestMethod03){s = "24", k = 1, minLength = 1;auto res = Solution().beautifulPartitions(s, k, minLength);AssertEx(1, res);}TEST_METHOD(TestMethod031){s = "24", k = 1, minLength =3;auto res = Solution().beautifulPartitions(s, k, minLength);AssertEx(0, res);}TEST_METHOD(TestMethod04){s = "783938233588472343879134266968", k = 4, minLength = 6;auto res = Solution().beautifulPartitions(s, k, minLength);AssertEx(4, res);}TEST_METHOD(TestMethod05){s = "242538614532395749146912679859", k = 1, minLength = 6;auto res = Solution().beautifulPartitions(s, k, minLength);AssertEx(1, res);}};
}

擴展閱讀

視頻課程

先學簡單的課程,請移步CSDN學院,聽白銀講師(也就是鄙人)的講解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成戰斗了,為老板分憂,請學習C#入職培訓、C++入職培訓等課程
https://edu.csdn.net/lecturer/6176

相關推薦

我想對大家說的話
《喜缺全書算法冊》以原理、正確性證明、總結為主。
按類別查閱鄙人的算法文章,請點擊《算法與數據匯總》。
有效學習:明確的目標 及時的反饋 拉伸區(難度合適) 專注
聞缺陷則喜(喜缺)是一個美好的愿望,早發現問題,早修改問題,給老板節約錢。
子墨子言之:事無終始,無務多業。也就是我們常說的專業的人做專業的事。
如果程序是一條龍,那算法就是他的是睛

測試環境

操作系統:win7 開發環境: VS2019 C++17
或者 操作系統:win10 開發環境: VS2022 C++17
如無特殊說明,本算法用**C++**實現。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/39434.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/39434.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/39434.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【C++ Primer Plus學習記錄】指針和const

可以用兩種不同的方式將const關鍵字用于指針。第一種方法是讓指針指向一個常量對象&#xff0c;這樣就可以防止使用該指針來修改所指向的值&#xff0c;第二種方法是將指針本身聲明為常量&#xff0c;這樣可以防止改變指針指向的位置。 首先&#xff0c;聲明一個指向常量的指針…

前后端防重復提交(續)

前文介紹過前后端防重復提交的基本場景&#xff0c;簡單的情況是只發起一個異步請求&#xff0c;如果有多個異步請求怎么操作呢&#xff1f;這個要分情況看下。 如果是后端服務器的接口支持一次傳遞多個申請&#xff0c;那么可以將任務放進數組中&#xff0c;發往后端。這是最好…

074、Python 關于實例方法、靜態方法和類方法

在Python中&#xff0c;類可以定義三種類型的方法&#xff1a;實例方法、靜態方法和類方法。每種方法都有其特定的用途和調用方式。 實例方法&#xff08;Instance Methods&#xff09; 定義&#xff1a;實例方法是綁定到類實例上的方法。它們必須有一個名為self的隱式第一個參…

golang 1.22特性之for loop

背景 go1.22版本 for loop每輪循環都生成新的變量. 原諒: https://tip.golang.org/doc/go1.22 Previously, the variables declared by a “for” loop were created once and updated by each iteration. In Go 1.22, each iteration of the loop creates new variables, to …

【C++11】自己封裝RAII類,有哪些坑點?帶你了解移動語義的真相

文章目錄 一、持有資源的類定義移動構造函數的要點1.普通內置類型與std::move2.常見的容器與std::move3.結構體&#xff1a;4.智能指針與std::move 參考 一、持有資源的類定義移動構造函數的要點 1.普通內置類型與std::move 在C中&#xff0c;std::move 主要用于對象的移動語…

Wireshark - tshark支持iptables提供數據包

tshark現在的數據包獲取方式有兩種&#xff0c;分別是讀文件、網口監聽&#xff08;af-packet原始套接字&#xff09;。兩種方式在包獲取上&#xff0c;都是通過讀文件的形式&#xff1b;存在文件io操作&#xff0c;在專門處理大流量的情境下&#xff0c; 我們復用wireshark去做…

Windows編程上

Windows編程[上] 一、Windows API1.控制臺大小設置1.1 GetStdHandle1.2 SetConsoleWindowInfo1.3 SetConsoleScreenBufferSize1.4 SetConsoleTitle1.5 封裝為Innks 2.控制臺字體設置以及光標調整2.1 GetConsoleCursorInfo2.2 SetConsoleCursorPosition2.3 GetCurrentConsoleFon…

python如何輸出list

直接輸出list_a中的元素三種方法&#xff1a; list_a [1,2,3,313,1] 第一種 for i in range(len(list_a)):print(list_a[i]) 1 2 3 313 1 第二種 for i in list_a:print(i) 1 2 3 313 1 第三種&#xff0c;使用enumerate輸出list_a方法&#xff1a; for i&#xff0c;j in enum…

Redis的使用(二)redis的命令總結

1.概述 這一小節&#xff0c;我們主要來研究一下redis的五大類型的基本使用&#xff0c;數據類型如下&#xff1a; redis我們接下來看一看這八種類型的基本使用。我們可以在redis的官網查詢這些命令:Commands | Docs,同時我們也可以用help 數據類型查看命令的幫助文檔。 2. 常…

數據結構 - C/C++ - 串

字符處理 C 特性 C語言中字符串存儲在字符數組中&#xff0c;以空字符\0結束。 字符串常量&#xff0c;const char* str "Hello"&#xff0c;存儲在只讀的數據段中。 布局 字符串在內存中是字符連續存儲的集合&#xff0c;最后一個字符為空字符(ASCII值為0)&…

opencascade AIS_InteractiveContext源碼學習7 debug visualization

AIS_InteractiveContext 前言 交互上下文&#xff08;Interactive Context&#xff09;允許您在一個或多個視圖器中管理交互對象的圖形行為和選擇。類方法使這一操作非常透明。需要記住的是&#xff0c;對于已經被交互上下文識別的交互對象&#xff0c;必須使用上下文方法進行…

【問題已解決】Vue管理后臺,點擊登錄按鈕,會發起兩次網絡請求(竟然是vscode Compile Hero編譯插件導致的)

問題 VueElement UI 做的管理后臺&#xff0c;點擊登錄按鈕&#xff0c;發現 接口會連續掉兩次&#xff0c;發起兩次網絡請求&#xff0c;但其他接口都是正常調用的&#xff0c;沒有這個問題&#xff0c;并且登錄按鈕也加了loading&#xff0c;防止重復點擊&#xff0c;于是開…

搜索引擎常用語法

引號 (" "): 用雙引號將詞組括起來&#xff0c;搜索引擎將返回包含完全相同短語的結果。 示例&#xff1a;"人工智能發展趨勢" 減號 (-): 在關鍵詞前加上減號可以排除包含特定詞語的結果。 示例&#xff1a;人工智能 -機器學習&#xff08;排除包含 “機器…

樸素貝葉斯解密:sklearn中的分類器工作原理

&#x1f4da; 樸素貝葉斯解密&#xff1a;sklearn中的分類器工作原理 在機器學習領域&#xff0c;樸素貝葉斯分類器因其簡單、高效而廣受歡迎。特別是在處理大量特征數據時&#xff0c;樸素貝葉斯表現出了卓越的性能。scikit-learn&#xff08;簡稱sklearn&#xff09;是Pyth…

JavaMySQL 學習(基礎)

目錄 Java CMD Java發展 計算機存儲規則 Java學習 switch新用法&#xff08;可以當做if來使用&#xff09; 數組定義 隨機數 Java內存分配 MySQL MySQL概述 啟動和停止 客戶端連接 數據模型 關系型數據庫 SQL SQL通用語法 SQL分類 DDL--數據定義語言 數據庫…

瀏覽器開發者工具輔助爬蟲開發

文章目錄 瀏覽器開發者工具輔助爬蟲開發打開開發者工具使用Network面板分析請求數據示例步驟&#xff1a; 使用Elements面板查看和修改DOM結構示例步驟&#xff1a; 使用Console面板調試JavaScript代碼示例步驟&#xff1a;示例代碼&#xff1a;1. 輸出日志信息2. 輸出對象信息…

Vue 與 React 區別

Vue.js和React是現代Web開發中兩種非常流行的前端框架&#xff0c;兩者在**核心概念、組件以及生態系統擴展性**等方面存在區別。具體分析如下&#xff1a; 1. **核心概念** - **Vue**&#xff1a;Vue是一個漸進式JavaScript框架&#xff0c;它致力于視圖層&#xff0c;易于上手…

左值右值, 左值引用右值引用,完美轉發

一. 左值和右值 左值: 可以取地址的對象 右值: 不可以取地址的對象 double x1.0, y 2.0; 1; // 字面量, 不可取地址, 是右值 x y; // 表達式返回值, 不可取地址, 是右值 max(x, y); // 傳值返回函數的返回值 (非引用返回)總結就是: 根據是否可以取地址來區分是左值還…

線程池666666

1. 作用 線程池內部維護了多個工作線程&#xff0c;每個工作線程都會去任務隊列中拿取任務并執行&#xff0c;當執行完一個任務后不是馬上銷毀&#xff0c;而是繼續保留執行其它任務。顯然&#xff0c;線程池提高了多線程的復用率&#xff0c;減少了創建和銷毀線程的時間。 2…

git修改已提交的commit注釋

在Git中修改已經提交的commit注釋通常有以下幾種情況和相應的方法&#xff1a; 1. 修改最后一次提交的注釋&#xff08;快速修正&#xff09; 如果你想要修改的是最后一次提交的注釋&#xff0c;可以使用 --amend 選項&#xff1a; git commit --amend這個命令會將你的暫存區…