UVA-1630 Folding (KMP、区间dp)

20 篇文章 0 订阅
5 篇文章 0 订阅

Time Limit: 3000MS Memory Limit: Unknown 64bit IO Format: %lld & %llu

 Status

Description

Download as PDF

Bill is trying to compactly represent sequences of capital alphabetic characters from `A' to `Z' by folding repeating subsequences inside them. For example, one way to represent a sequence `AAAAAAAAAABABABCCD' is `10(A)2(BA)B2(C)D'. He formally defines folded sequences of characters along with the unfolding transformation for them in the following way: 

  • A sequence that contains a single character from `A' to `Z' is considered to be a folded sequence. Unfolding of this sequence produces the same sequence of a single character itself. 
  • If S and Q are folded sequences, then SQ is also a folded sequence. If S unfolds to S' and Q unfolds to Q', then SQ unfolds to S'Q'
  • If S is a folded sequence, then X(S) is also a folded sequence, where X is a decimal representation of an integer number greater than 1. If S unfolds to S', then X(S) unfolds to S' repeated X times. 

According to this definition it is easy to unfold any given folded sequence. However, Bill is much more interested in the reverse transformation. He wants to fold the given sequence in such a way that the resulting folded sequence contains the least possible number of characters. 

Input 

Input file contains several test cases, one per line. Each of them contains a single line of characters from `A' to `Z' with at least 1 and at most 100 characters. 

Output 

For each input case write a different output line. This must be a single line that contains the shortest possible folded sequence that unfolds to the sequence that is given in the input file. If there are many such sequences then write any one of them. 

Sample Input 

AAAAAAAAAABABABCCD
NEERCYESYESYESNEERCYESYESYES

Sample Output 

9(A)3(AB)CCD
2(NEERC3(YES))


题意:

给出一个字符串,折叠成一个尽量短的串,串的每一个字符都算作长度。

分析:

借鉴自:http://www.cnblogs.com/AOQNRMGYXLMV/p/4781849.html

用d[i][j]表示i-j区间内最短字符串,进行状态更新。

思路很简单,但实现起来技巧很多,借鉴了大神的代码。

先是温习了一下KMP,然后发现自己想法和写法和别人的比都太逗了,就按照大神思路抄了下来。

按长度从小到大对字符串的子串进行简化,然后合并更新,最后得到最大串。其中KMP主要是对不同长度的子串进行处理,找到子串结尾的next数组对应大小,根据KMP思想可以得到对于长度为L的字符串,next[L+1]得到的恰好是第L个字符所对应的前一个字符(因为字符串是从0开头的),所以L-next(L+1)就是该子串中重复次数最高的子子串的长度大小(模拟一下就明白了)。

然后就是区间更新合并,思路不难,实现有点技巧,值得学习。

#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <fstream>
#include <vector>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3f
const int N=105;
const int mod=1e9+7;

int nex[N];
char s[N],t[N];
string d[N][N];

string ToString(int x) {
    string ans="";
    while (x) {
        ans+=(char)('0'+x%10);
        x/=10;
    }
    reverse(ans.begin(), ans.end());
    return ans;
}

void getNext(char *b) {
    nex[0]=nex[1]=0;
    int len=strlen(b);
    int j=0;
    for (int i=1; i<len; i++) {
        while (j>0&&b[i]!=b[j]) j=nex[j];
        if (b[i]==b[j]) j++;
        nex[i+1]=j;
    }
}

int main() {
    while (scanf("%s",&s)==1) {
        int len=strlen(s);
        for (int i=0; i<len; i++) d[i][i]=string("")+s[i];
        
        for (int l=2; l<=len; l++) {
            for (int i=0; i+l-1<len; i++) {
                int j=i+l-1;
                d[i][j]="";
                for (int k=i; k<=j; k++) {
                    d[i][j]+=s[k];
                    t[k-i]=s[k];
                }
                t[j-i+1]='/0';
                getNext(t);
                if (l%(l-nex[l])==0) {
                    int cycle=l-nex[l];
                    string x=ToString(l/cycle);
                    x+='(';
                    x+=d[i][i+cycle-1];
                    x+=')';
                    if (x.length()<d[i][j].length()) {
                        d[i][j]=x;
                    }
                }
                
                for (int k=i; k<j; k++) {
                    if (d[i][k].length()+d[k+1][j].length()<d[i][j].length()) {
                        d[i][j]=d[i][k]+d[k+1][j];
                    }
                }
            }
        }
        cout<<d[0][len-1]<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值