UVALive 3363 String Compression
2013-08-05 15:59
204 查看
题目大意:给你一种压缩字符串的方式,可以将相同的连续的重复字串表示成k(s)的形式,比如“nowletsgogogoletsgogogoandrunrunrun”,就可以压缩为“ now2(lets3(go))and3(run)”,给你一个字符串,问你用这种方法压缩,最少的字符个数是多少?
思路:用d[ i ][ j ] 来表示第i个字符到第j个字符的最小长度,那么d[ i ][ j ] = min(min(d[ i ][ k ]+d[ k + 1][ j ]),min(d[ i ][ i + dis - 1 ] + digs + 2)),其中第一部分为枚举将i~j 这部分进行拆分,而光这样转移是不够的,因为枚举的两个部分长度都是小于总长度(j-i+1)的,还有一种情况就是整段都可以是由一个字串重复组成,所以第二部分是关键,dis是指枚举的最小的重复字串的长度,当然dis满足(j - i
+1)%dis == 0 && i~j这一段可以是i~i+dis-1 这一段重复产生的,digs是指前面那个数字的长度,10以下是1,10~99是2,100~999是3,而2指的是两边的括号。
好吧,我承认我看到这道题的时候完全没有思路,我先开始只想到开一维的d[ i ] 来表示状态,感觉状态转移太复杂了,也就没有继续往下做了,想想变成d[ i ][ j ] 好像也很复杂的样子。。 后来去搜题解,找了半天找不到,后来有幸找到了一份代码,研究了一下,思路、代码很快就懂了,之后想想也不是很难,可自己就是想不到啊,而且每次都不会继续深入下去,感觉太烦就不往下想了。自己想到,和看题解看会差距是很大的!火候还差很多啊,继续多想,多练吧。。 还有一点,不到迫不得已,不搜题解!
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 222;
char str[MAXN];
int d[MAXN][MAXN];
int nums[MAXN];
int check(int l,int r,int dis)
{
for(int i = 0;i<dis;i++)
{
for(int j = l+i;j<=r;j += dis)
{
if(str[l+i]!=str[j]) return 0;
}
}
return 1;
}
void dp(int l,int r)
{
int len = r - l + 1 ;
int ret = len ;
for(int i = l;i<r;i++) ret = min(ret,d[l][i]+d[i+1][r]);
for(int dis = 1;dis<=len/2;dis++)
{
if(len%dis==0)
{
if(check(l,r,dis))
{
ret = min(ret,nums[len/dis]+d[l][l+dis-1]+2);
}
}
}
d[l][r] = ret;
}
void init()
{
for(int i = 0;i<10;i++)
nums[i] = 1;
for(int i = 10;i<100;i++)
nums[i] = 2;
for(int i = 100;i<MAXN;i++)
nums[i] = 3;
}
int main()
{
init();
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",str);
int len = strlen(str);
for(int dis = 1;dis<=len;dis++)
{
for(int i = 0;i<len;i++)
{
int j = i+dis-1;
if(j>len) break;
dp(i,j);
}
}
printf("%d\n",d[0][len-1]);
}
return 0;
}
思路:用d[ i ][ j ] 来表示第i个字符到第j个字符的最小长度,那么d[ i ][ j ] = min(min(d[ i ][ k ]+d[ k + 1][ j ]),min(d[ i ][ i + dis - 1 ] + digs + 2)),其中第一部分为枚举将i~j 这部分进行拆分,而光这样转移是不够的,因为枚举的两个部分长度都是小于总长度(j-i+1)的,还有一种情况就是整段都可以是由一个字串重复组成,所以第二部分是关键,dis是指枚举的最小的重复字串的长度,当然dis满足(j - i
+1)%dis == 0 && i~j这一段可以是i~i+dis-1 这一段重复产生的,digs是指前面那个数字的长度,10以下是1,10~99是2,100~999是3,而2指的是两边的括号。
好吧,我承认我看到这道题的时候完全没有思路,我先开始只想到开一维的d[ i ] 来表示状态,感觉状态转移太复杂了,也就没有继续往下做了,想想变成d[ i ][ j ] 好像也很复杂的样子。。 后来去搜题解,找了半天找不到,后来有幸找到了一份代码,研究了一下,思路、代码很快就懂了,之后想想也不是很难,可自己就是想不到啊,而且每次都不会继续深入下去,感觉太烦就不往下想了。自己想到,和看题解看会差距是很大的!火候还差很多啊,继续多想,多练吧。。 还有一点,不到迫不得已,不搜题解!
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 222;
char str[MAXN];
int d[MAXN][MAXN];
int nums[MAXN];
int check(int l,int r,int dis)
{
for(int i = 0;i<dis;i++)
{
for(int j = l+i;j<=r;j += dis)
{
if(str[l+i]!=str[j]) return 0;
}
}
return 1;
}
void dp(int l,int r)
{
int len = r - l + 1 ;
int ret = len ;
for(int i = l;i<r;i++) ret = min(ret,d[l][i]+d[i+1][r]);
for(int dis = 1;dis<=len/2;dis++)
{
if(len%dis==0)
{
if(check(l,r,dis))
{
ret = min(ret,nums[len/dis]+d[l][l+dis-1]+2);
}
}
}
d[l][r] = ret;
}
void init()
{
for(int i = 0;i<10;i++)
nums[i] = 1;
for(int i = 10;i<100;i++)
nums[i] = 2;
for(int i = 100;i<MAXN;i++)
nums[i] = 3;
}
int main()
{
init();
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",str);
int len = strlen(str);
for(int dis = 1;dis<=len;dis++)
{
for(int i = 0;i<len;i++)
{
int j = i+dis-1;
if(j>len) break;
dp(i,j);
}
}
printf("%d\n",d[0][len-1]);
}
return 0;
}
相关文章推荐
- UVA 1351 UVALive 3363 String Compression
- UVALive - 3363 String Compression (区间DP)
- UVAlive-3363 String Compression
- UVALive - 3363 String Compression 区间DP
- uvalive 3363(区间dp)
- UVALive 6658 - Fiasco(BFS)
- UVALive 6955 Finding Lines(奇特的过题方式)
- uvalive 2572
- UVALive 6838 Flipping Parentheses (线段树)
- UVALive 6837 (最小生成树)
- UVALive 3305Tour(双调DP)
- UVALive 4244 Party Party Party(HDU 2779 && Sicily 1663)
- UVALive - 3401 Colored Cubes 枚举
- UVALive 3490 Generator(AC自动机+dp+高斯消元)
- UVALive - 4764 Bing it dp
- UVALive 3720 UVA 1393 Highways
- UVALIVE 5000 Underwater Snipers(二分+贪心)
- UVALive 6844 Combination(打表找规律)
- UVALive - 5902 Movie collection
- Vampire Numbers - UVALive 5779 暴力打表