题目描述
折叠的定义如下:
一个字符串可以看成它自身的折叠。记作S = S
X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) = SSSS…S(X个S)。
如果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一定是dp[l][r], 表示l,r区间内的最短折叠,转移就简单了,从l遍历到r看看后半部分能不能整除前半部分,然后用哈希判断一下是不是能叠,能叠的话直接叠起来,要注意的是重复次数的位数是会变的,我原来是直接前半部长度+3,表示加了x,(,),后来看了洛谷评论区发现AAAAAAAAAA (10个)的数据。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[200][200];
long long has[400];
long long pp[500];
const int p = 151;
long long getHas(int l,int r)
{
return has[r] - has[l-1] * pp[r- l +1];
}
signed main()
{
char s[200];
cin >> (s+1);
pp[0] = 1;
has[0] = 1;
for (int i = 1; i <= 200; ++i) {
pp[i]= pp[i-1] * p;
has[i] = has[i-1] * p + s[i] - 'A';
}
memset(dp,0x3f,sizeof(dp));
int l = strlen(s+1);
for (int len = 1; len <= l; ++len) {
for (int i = 1; i + len - 1<= l; ++i) {
int end = i+len-1;
if (len == 1)
{
dp[i][end] =1;continue;
}
for (int j = i; j < end; ++j) {
int pre = j - i +1;
int suf = end - j;
if (suf%pre == 0)
{
int tmp = getHas(i,j);
int nowp = j+1;
int nowe = nowp+pre-1;
int mul = 1;
while (nowe<=end)
{
if (tmp != getHas(nowp,nowe))
break;
nowe+=pre;
nowp+=pre;
mul+=1;
}
int add = 0;
while (mul)
{
add++;
mul/=10;
}
if (nowe == end + pre) {
dp[i][end] = min(dp[i][end],min(dp[i][j]+dp[j+1][end],dp[i][j]+2+add));
} else
dp[i][end] = min(dp[i][end],dp[i][j]+dp[j+1][end]);
}else
dp[i][end] = min(dp[i][end],dp[i][j]+dp[j+1][end]);
}
}
}
cout << dp[1][l] << endl;
}
这道题目要求求解一个字符串的最短折叠形式。通过定义,字符串折叠是自身连接的串的折叠。题目提供了一个例子说明如何进行折叠,并给出了一种特定情况的示例。解决此问题使用动态规划方法,定义dp[l][r]表示字符串从位置l到r的最短折叠。在转移过程中,检查字符串后半部分是否能被前半部分整除,并使用哈希检查折叠可行性。注意,重复次数的位数可能会改变,需要特别考虑如'AAAAAAAAAA'这样的特殊情况。

被折叠的 条评论
为什么被折叠?



