1. 程式人生 > 其它 >【P4302 [SCOI2003]字串摺疊】題解

【P4302 [SCOI2003]字串摺疊】題解

題目連結

題目

摺疊的定義如下:
  1. 一個字串可以看成它自身的摺疊。記作S = S
  2. X(S)是X(X>1)個S連線在一起的串的摺疊。記作X(S) = SSSS…S(X個S)。
  3. 如果A = A’, B = B’,則AB = A’B’ 例如,因為3(A) = AAA, 2(B) = BB,所以3(A)C2(B) = AAACBB,而2(3(A)C)2(B) = AAACAAACBB

給一個字串,求它的最短折疊。例如AAAAAAAAAABABABCCD的最短折疊為:9(A)3(AB)CCD。

思路

區間dp

首先必然列舉中間點,然後左右合併。

也可以列舉迴圈節,然後暴力判斷。

判斷之前先看一下是否為長度的因數,如果暴力後是否能使答案更小,然後就不會被卡。

原則上時間複雜度 \(O(n^4)\),然後必然卡不滿,相當於 \(O(n^3)\) 加個小常數,最壞也就 \(O(n^3\sqrt n)\)

tips:與UVA1351雙倍經驗哦!

Code

// Problem: UVA1351 String Compression
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA1351
// Memory Limit: 0 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 210
int n, m, i, j, k; 
int f[N][N]; 
char s[N]; 
int l, r, len, t; 

int length(int x)
{
	int ans=0; 
	while(x) ++ans, x/=10; 
	return ans; 
}

int pan(int l, int r, int k)
{
	for(i=l+k; i<=r; ++i)
		if(s[i]!=s[i-k]) return 0; 
	return 1; 
}

int check(int l, int r, int k)
{
	if(2+f[l][l+k-1]+length(len/k)>=f[l][r]) return 999; 
	if(pan(l, r, k)) return 2+f[l][l+k-1]+length(len/k); 
	return 999; 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	// t=read(); 
	t=1; 
	while(t--)
	{
		scanf("%s", s+1); 
		n=strlen(s+1); 
		for(i=1; i<=n; ++i) f[i][i]=1; 
		for(len=2; len<=n; ++len)
			for(l=1, r=l+len-1; r<=n; ++l, ++r)
			{
				f[l][r]=len; 
				for(k=l; k<r; ++k) f[l][r]=min(f[l][r], f[l][k]+f[k+1][r]); 
				for(k=1; k*k<=len; ++k)
					if(len%k==0)
					{
						f[l][r]=min(f[l][r], check(l, r, k)); 
						f[l][r]=min(f[l][r], check(l, r, len/k)); 
					}
			}
		printf("%d\n", f[1][n]); 
	}
	
	return 0; 
}