[Luogu P2470] [BZOJ 1068] [SCOI2007]壓縮

洛谷傳送門

BZOJ傳送門

題目描述

給一個由小寫字母組成的字符串,我們可以用一種簡單的方法來壓縮其中的重複信息。壓縮後的字符串除了小寫字母外還可以(但不必)包含大寫字母RRMM,其中MM標記重複串的開始,RR重複從上一個MM(如果當前位置左邊沒有MM,則從串的開始算起)開始的解壓結果(稱爲緩衝串)。

bcdcdcdcd可以壓縮爲bMcdRR,下面是解壓縮的過程:

已經解壓的部分 解壓結果 緩衝串
b b b
bM b .
bMc bc c
bMcd bcd cd
bMcdR bcdcd cdcd
bMcdRR bcdcdcdcd cdcdcdcd

輸入輸出格式

輸入格式:

輸入僅一行,包含待壓縮字符串,僅包含小寫字母,長度爲nn

輸出格式:

輸出僅一行,即壓縮後字符串的最短長度。

輸入輸出樣例

輸入樣例#1:

aaaaaaa

輸出樣例#1:

5

輸入樣例#2:

bcdcdcdcdxcdcdcdcd

輸出樣例#2:

12

說明

在第一個例子中,解爲aaaRa,在第二個例子中,解爲bMcdRRxMcdRR。

【限制】

50%的數據滿足:1n201\le n\le 20

100%的數據滿足:1n501\le n\le 50

解題分析

dp[i]dp[i]表示長度爲ii的前綴的最短長度, 每次我們考慮向後添加一段形如M***R**的段, 然後轉移。

判字符串是否相等直接用字符串哈希, 然後每次前一半和後一半相同就加上一個RR, 前面長度減半,然後對答案取minmin。 否則在這段序列後加一個單獨的字母。

總複雜度O(N3)O(N^3)

注意特判l=1l=1的情況, 因爲第一段不需要在前面加上MM

代碼如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <climits>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define BASE 233
#define MOD 998244353
#define MX 55
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int len;
int dp[MX], hs[MX], base[MX];
char str[MX];
IN int seg(R int l, R int r) {return (hs[r] - 1ll * hs[l - 1] * base[r - l + 1] % MOD + MOD) % MOD;}
struct INFO {bool typ; int ret;};
IN INFO get(R int l, R int r)
{
	int best = INT_MAX, sym = 1, avai = r - l + 1, mid;
	W (l < r)
	{
		mid = l + r >> 1;
		W (l < r && seg(l, mid) != seg(mid + 1, r)) --r, mid = l + r >> 1, ++sym;
		if (l == r) break;
		++sym, r = mid;
		best = min(best, sym + r - l + 1);
	}
	return (best <= avai ? (INFO){false, best} : (INFO){true, avai});
}
int main(void)
{
	scanf("%s", str + 1);
	len = std::strlen(str + 1);
	base[0] = 1;
	for (R int i = 1; i <= len; ++i)
	{
		base[i] = 1ll * base[i - 1] * BASE % MOD;
		hs[i] = (1ll * BASE * hs[i - 1] % MOD + str[i]) % MOD;
	}
	INFO res;
	for (R int i = 1; i <= len; ++i)
	{
		res = get(1, i);
		if (!res.typ) dp[i] = res.ret - 1;
		else dp[i] = res.ret;
		for (R int j = 2; j <= i; ++j)
		{
			res = get(j, i);
			dp[i] = min(dp[i], dp[j - 1] + res.ret);
		}
	}
	printf("%d\n", dp[len]);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章