BZOJ 1090 字符串折叠(区间DP)

很明显的区间DP,设dp[l][r]表示[l,r]区间的字符串折叠后的最小长度。

可以通过两种方向转移,dp[l][r]=min(dp[l][i]+dp[i+1][r]).

另一种是折叠,dp[l][r]=min(dp[l][l+k-1]+cal((r-l+1)/k)+2).其中k是能整除(r-l+1)的数且区间能够折叠成k份,cal()函数计算数字的位数。

另外用了线段树维护hash值,可以每次验证logn。

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-8
# define MOD 1000000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())==‘-‘) flag=1;
    else if(ch>=‘0‘&&ch<=‘9‘) res=ch-‘0‘;
    while((ch=getchar())>=‘0‘&&ch<=‘9‘)  res=res*10+(ch-‘0‘);
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar(‘-‘); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+‘0‘);
}
const int N=105;
//Code begin...

char s[N];
int dp[N][N], n;
LL seg[N<<2], power[N];

void push_up(int p, int L){seg[p]=(seg[p<<1]*power[L>>1]+seg[p<<1|1])%MOD;}
void init(int p, int l, int r){
    if (l<r) {
        int mid=(l+r)>>1;
        init(lch); init(rch); push_up(p,r-l+1);
        return ;
    }
    seg[p]=s[l];
}
LL query(int p, int l, int r, int L, int R)
{
    if (L>r||R<l) return 0;
    if (L<=l&&R>=r) return seg[p]*power[R-r]%MOD;
    int mid=(l+r)>>1;
    return (query(lch,L,R)+query(rch,L,R))%MOD;
}
int qiu(int x){
    if (x<10) return 1;
    else if (x<100) return 2;
    else return 3;
}
int cal(int l, int r, int k)
{
    LL val=query(1,1,n,l,l+k-1);
    bool flag=1;
    for (int i=l+k; i<=r; i+=k) if (val!=query(1,1,n,i,i+k-1)) {flag=0; break;}
    if (!flag) return INF;
    else return dp[l][l+k-1]+qiu((r-l+1)/k)+2;
}
int dfs(int l, int r)
{
    if (~dp[l][r]) return dp[l][r];
    if (l==r) return dp[l][r]=1;
    int ans=r-l+1;
    FO(i,l,r) ans=min(ans,dfs(l,i)+dfs(i+1,r));
    FOR(i,1,(r-l+1)/2) if ((r-l+1)%i==0) ans=min(ans,cal(l,r,i));
    return dp[l][r]=ans;
}
int main ()
{
    mem(dp,-1);
    power[0]=1; FOR(i,1,100) power[i]=power[i-1]*31%MOD;
    scanf("%s",s+1);
    n=strlen(s+1);
    init(1,1,n);
    dfs(1,n);
    printf("%d\n",dp[1][n]);
    return 0;
}

时间: 2024-10-27 19:47:43

BZOJ 1090 字符串折叠(区间DP)的相关文章

BZOJ 1090 字符串折叠(区间DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1090 题意:字符串AAAAAAAAAABABABCCD的最短折叠为9(A)3(AB)CCD,注意数字的长度和圆括号都算最后长度.求一种折叠方式使得总长度最小. 思路:f[L][R]=min(R-L+1,f[L][i]+f[i+1][R]),另外若[L,R]能由[i+1,R]重复若干次,则也可用折叠后的长度更新f[L][R]. char s[N]; int f[N][N],n; int

BZOJ 1090 字符串折叠(Hash + DP)

题目链接 字符串折叠 区间DP.f[l][r]为字符串在区间l到r的最小值 正常情况下 f[l][r] = min(f[l][r], f[l][l + k - 1] + f[l + k][r]); 当l到r以k为周期时 f[l][r] = min(f[l][r], 2 + sz(k) + f[l][l + (r - l + 1) / k - 1]); 判重的时候为了方便我用了哈希……当然其他方法应该也是可以的~ #include <bits/stdc++.h> using namespace

BZOJ 1090: [SCOI2003]字符串折叠( 区间dp )

按照题意dp...dp(l, r) = min{ dp(l, x) + dp(x+1, r) , 折叠(l, r) } 折叠(l, r)我是直接枚举长度然后哈希判.. -------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int maxn = 109; con

BZOJ 1090 SCOI 2003 字符串折叠 区间DP

题目大意:给出一个字符串,在不改变这个字符串的内容的情况下可以将它进行折叠,具体见题里说的吧.问这个字符串最短可以折叠成多长. 思路:数据范围才100,怎么暴力怎么搞.首先是一个区间DP,设f[i][j]为字符串从i开始到j最短可以折叠成多短.要用到体中的折叠的方法,其实只需要暴力枚举这一段折叠成几段,然后用hash判定一下就行了. 当然不要忘了正常的区间DP. CODE: #include <cstdio> #include <cstring> #include <iost

【BZOJ-1090】字符串折叠 区间DP + Hash

1090: [SCOI2003]字符串折叠 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1127  Solved: 737[Submit][Status][Discuss] Description 折叠的定义如下: 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) =

BZOJ 1055 玩具取名(区间DP)

很显然的区间DP,定义dp[i][j][k], 如果dp[i][j][k]=1表示字符串[i,j]可以组成k字符. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map>

BZOJ 1068 [SCOI2007]压缩 区间DP

题意:链接 方法:区间DP 解析: MD写题解(吐槽)写到一半markdown挂了什么鬼! 要不要这样!你知道我的内心是什么样的吗! 吐槽,啊呸,写题解写到一半突然丢失了我的内心是崩溃的好吗! 来我们重新写题解(吐槽) 这道题我刚开始列了个瞎(和谐)动规(二维的裸区间) 加上乱七八糟的判断是否有M后,居然有交叉! 一定是我逻辑错误,对就是这样! 后来又是一顿瞎(和谐)搞之后,代码抽的爆炸,然后我一测,c-free挂掉- - 过了一个小时后,我选择死亡. 然后看了一眼hzw的题解. 看到那个三维之

HihoCOder1323 : 回文字符串(区间DP)

回文字符串 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个字符串 S ,最少需要几次增删改操作可以把 S 变成一个回文字符串? 一次操作可以在任意位置插入一个字符,或者删除任意一个字符,或者把任意一个字符修改成任意其他字符. 输入 字符串 S.S 的长度不超过100, 只包含'A'-'Z'. 输出 最少的修改次数. 样例输入 ABAD 样例输出 1 区间DP水题,见铺垫:密码脱落. #include<cstdio> #include<cstdlib

【bzoj2121】字符串游戏 区间dp

题目描述 给你一个字符串L和一个字符串集合S,如果S的某个子串在S集合中,那么可以将其删去,剩余的部分拼到一起成为新的L串.问:最后剩下的串长度的最小值. 输入 输入的第一行包含一个字符串,表示L. 第二行包含一个数字n,表示集合S中元素个数. 以下n行,每行一个字符串,表示S中的一个元素. 输入字符串都只包含小写字母. 输出 输出一个整数,表示L的最短长度. 样例输入 aaabccd3acabcaaa 样例输出 2 题解 我们考虑:每次删除连续的一段,对应到原串上即:删除 $[l,r]$ 中所