字节--字母交换
字節(jié)–字母交換
文章目錄
- 字節(jié)--字母交換
- 一、題目描述
- 二、分析
- 三、代碼
一、題目描述
字符串S由小寫字母構(gòu)成,長度為n。定義一種操作,每次都可以挑選字符串中任意的兩個(gè)相鄰字母進(jìn)行交換。詢問在至多交換m次之后,字符串中最多有多少個(gè)連續(xù)的位置上的字母相同?
- 輸入描述:
- 輸出描述:
二、分析
- 這題算是這場筆試最難的題了,區(qū)間動(dòng)態(tài)規(guī)劃。
- 要求移動(dòng)后形成的最長連續(xù)子串,這個(gè)最長連續(xù)子串可能全是a或b……c。因此,這里需要枚舉移動(dòng)后形成的最長連續(xù)子串里面所包含的字母;
- 確定了里面包含的字母,就可以專注于這個(gè)字母了,也就是說其余的字母都是沒有用的,把它們從序列中挖掉;
- 然后就值剩下目標(biāo)字母了,目標(biāo)字母離散地分布在序列中,因此,再離散化一下,搞完之后會(huì)生成一個(gè)行的序列,之后的動(dòng)態(tài)規(guī)劃就在新的序列上進(jìn)行。
- 下面的圖片表達(dá)了上述過程:
- 現(xiàn)在新的序列pos看起來是合在了一起,形成了最長連續(xù)子序列,但是,形成這些連續(xù)序列所需要的操作次數(shù)是多少呢?如果操作次數(shù)大于m,那么該序列就是不滿足要求的;
- 因此,這里面就可以得出區(qū)間動(dòng)態(tài)規(guī)劃了,先從小到大枚舉段長,依次求得該段長的所有子序列的操作次數(shù),并判斷是否小于等于m,如果滿足要求,就更新答案。
- 從小到大枚舉段長是為了利用子問題的結(jié)果;dp[i][j]表示把pos[i]和pos[j]之間的目標(biāo)字母移動(dòng)到一起,形成j - i + 1長度的連續(xù)子序列所需要的操作次數(shù);
- 狀態(tài)轉(zhuǎn)移方程:dp[i][i + len - 1] = dp[i + 1][i + len - 2] + pos[i + len - 1] - pos[i] - len + 1;
- 依據(jù)是|x?a|+|x?b||x?a|+|x?b|在什么時(shí)候取得最小值。用最小的移動(dòng)次數(shù)把兩個(gè)目標(biāo)字母移動(dòng)到一起的方法就是把兩個(gè)目標(biāo)字母都往中間靠,狀態(tài)轉(zhuǎn)移方程就是根據(jù)這個(gè)來的
- 先把pos[i + 1] ~ pos[i + len - 2]之間的目標(biāo)字母移動(dòng)到一起,這個(gè)移動(dòng)次數(shù)就是dp[i + 1][i + len - 2],然后把兩個(gè)端點(diǎn)pos[i]和pos[i + len -1]處的目標(biāo)字母往中間靠,所需要的移動(dòng)次數(shù)是pos[i + len - 1] - pos[i] - len + 1。
三、代碼
#include <bits/stdc++.h> using namespace std;int main() {string s;int m;while (cin >> s >> m) {int ans = 1;//枚舉每一個(gè)字符for (char c = 'a'; c <= 'z'; c++) {//構(gòu)建pos數(shù)組vector<int> pos;for (int i = 0; i < (int)s.size(); i++)if (c == s[i])pos.push_back(i);//過濾掉只出現(xiàn)一次的字符if (pos.size() < 2)continue;//dp數(shù)組int ret = 1;vector<vector<int> > dp(pos.size(), vector<int>(pos.size(), 0));for (int len = 2; len <= (int)pos.size(); ++len) {for (int i = 0; i + len - 1 < (int)pos.size(); i++) {dp[i][i + len - 1] = dp[i + 1][i + len - 2] + pos[i + len - 1] - pos[i] - len + 1;if (dp[i][i + len - 1] <= m)ret = len;}}ans = max(ans, ret);}cout << ans << endl;}return 0; }總結(jié)
- 上一篇: 字节--手串
- 下一篇: 二叉树构建及双向链表