题解 P4302 【[SCOI2003]字符串折叠】
讲讲我的做法
题目大意:对一个字符串进行折叠是它长度最小
看一眼数据范围:哇!字符串长度不超过100!这是一道省选题,不可能给你太宽裕的时限,所以,题目基本暗示你要用\(n^{3}\)多一些的算法复杂度。
这是一道最优化的题目,常见求最优化问题的算法比如贪心,模拟,枚举我都想不出什么好办法,唯独觉得像一道区间\(dp\)
区间\(dp\)的分析
解释状态
我们用\(f[i][j]\)表示\(i\)到\(j\)这个区间内最小的长度
首先,我们可以把\(i\)~\(j\)这个区间的字符串拆成2部分处理
就有了这段代码:
for(int l=2;l<=n;l++)
for(int i=1,j=i+l-1;j<=n;i++,j++)
for(int k=i;k<j;k++)
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
当然我用了字符串,然后加空格,这样更加符合人脑思维
也有同学喜欢用字符数组,我也写了这样的一段代码
for(int l=2;l<=n;l++){
for(int i=0,j=i+len-1;j<n;i++,j++){
for(int k=i;k<j;k++)
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
}
}
折叠
至于如何判断能否折叠,我呢用了一个函数——\(check\),来检查一下是否可以折叠
字符串代码:
bool check(int l,int r,int len){
for(int i=l;i<=r;i++)
if(st[i]!=st[(i-l)%len+l])return false;
return true;
}
字符数组代码
bool check(char s[],int n,int len){
for(int i=len;i<n;i++)
if(s[i]!=s[i%len])return false;
return true;
}
判断好了是否可以折叠,我们就可以去写状态了,从\(i\)~\(j\),判断区间折叠的循环节
字符串代码
for(int l=2;l<=n;l++){
for(int i=1,j=i+l-1;j<=n;i++,j++){
for(int k=i;k<j;k++)
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
for(int k=i;k<j;k++){
int len=k-i+1;
if(l%len!=0)continue;
if(check(i,j,len))f[i][j]=min(f[i][j],f[i][k]+2+m[l/len]);
}
}
}
字符数组代码
for(int l=2;l<=n;l++){
for(int i=1,j=i+l-1;j<n;i++,j++){
for(int k=i;k<j;k++)
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
for(int k=i;k<j;k++){
int len=k-i+1;
if(l%len!=0)continue;
if(check(s+i,l,len))f[i][j]=min(f[i][j],f[i][k]+2+m[l/len]);
}
}
}
边界条件以及初始化
刚刚的代码里出现里\(m\),现在我就来解释一下\(m\)数组是干什么的
\(m[i]\)的值表示的是i的位数,因为字符串的长度跟数字的位数有关
提到了\(m\)数组的左右自然由于提及如何用代码实现
我用的是最简单的方法,\(for\)循环扫,注意:100也要赋值,万一数据给你100个同样的字符
for(int i=1;i<=9;i++)m[i]=1;
for(int i=10;i<=99;i++)m[i]=2;
m[100]=3;
现在我们想一想初始化怎么做?
显然,\(f[i][i]=1\),如何数组的初值要设为\(INF\)
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)f[i][i]=1;
现在我们已经做完了所有的步骤,让我们看一看完整代码吧
字符串代码
#include <bits/stdc++.h>
using namespace std;
string st;
int n,m[110],f[110][110];
bool check(int l,int r,int len){
for(int i=l;i<=r;i++)
if(st[i]!=st[(i-l)%len+l])return false;
return true;
}
int main(){
cin>>st;
n=st.size();
st=' '+st;
for(int i=1;i<=9;i++)m[i]=1;
for(int i=10;i<=99;i++)m[i]=2;
m[100]=3;
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)f[i][i]=1;
for(int l=2;l<=n;l++){
for(int i=1,j=i+l-1;j<=n;i++,j++){
for(int k=i;k<j;k++)
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
for(int k=i;k<j;k++){
int len=k-i+1;
if(l%len!=0)continue;
if(check(i,j,len))f[i][j]=min(f[i][j],f[i][k]+2+m[l/len]);
}
}
}
printf("%d",f[1][n]);
return 0;
}
字符数组代码
#include <bits/stdc++.h>
using namespace std;
char s[110];
int n,m[110],f[110][110];
bool check(char s[],int n,int len){
for(int i=len;i<n;i++)
if(s[i]!=s[i%len])return false;
return true;
}
int main(){
scanf("%s",s);
n=strlen(s);
for(int i=1;i<=9;i++)m[i]=1;
for(int i=10;i<=99;i++)m[i]=2;
m[100]=3;
memset(f,0x3f,sizeof(f));
for(int i=0;i<n;i++)f[i][i]=1;
for(int l=2;l<=n;l++){
for(int i=1,j=i+l-1;j<n;i++,j++){
for(int k=i;k<j;k++)
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
for(int k=i;k<j;k++){
int len=k-i+1;
if(l%len!=0)continue;
if(check(s+i,l,len))f[i][j]=min(f[i][j],f[i][k]+2+m[l/len]);
}
}
}
printf("%d",f[0][n-1]);
return 0;
}
时间复杂度
看上去我们连续套了4个循环,然而真的时间复杂度就达到了\(n^{4}\)吗?其实不是的
首先\(n^{3}\)是肯定要的,那么为什么时间复杂度没有达到\(n^{4}\)呢!
原因在于我们的continue剪枝,它能够给这个\(n^{4}\)的复杂度加上一个\(log\)
为什么?
我们要check的显然是\(l\)的因数,然而\(l\)的因数个数\(\approx\) \(\log{l}\)
现实当中的复杂度还会更小,因为\(check\)的复杂度没有到\(O(n)\),它不是从头开始,没有到头结束,并且一旦发现错误后会直接\(return\)
其实可以把里面的2个循环并成一个循环,但为了让大家看的更清楚,就不演示了
题解 P4302 【[SCOI2003]字符串折叠】的更多相关文章
- P4302 [SCOI2003]字符串折叠
题目描述 折叠的定义如下: 一个字符串可以看成它自身的折叠.记作S = S X(S)是X(X>1)个S连接在一起的串的折叠.记作X(S) = SSSS…S(X个S). 如果A = A’, B = ...
- luogu P4302 [SCOI2003]字符串折叠
题目描述 折叠的定义如下: 一个字符串可以看成它自身的折叠.记作S = S X(S)是X(X>1)个S连接在一起的串的折叠.记作X(S) = SSSS-S(X个S). 如果A = A', B = ...
- 洛谷P4302 [SCOI2003]字符串折叠(区间dp)
题意 题目链接 Sol 裸的区间dp. 转移的时候枚举一下断点.然后判断一下区间内的字符串是否循环即可 `cpp #include<bits/stdc++.h> #define Pair ...
- [SCOI2003]字符串折叠(区间dp)
P4302 [SCOI2003]字符串折叠 题目描述 折叠的定义如下: 一个字符串可以看成它自身的折叠.记作S = S X(S)是X(X>1)个S连接在一起的串的折叠.记作X(S) = SSSS ...
- BZOJ 1090: [SCOI2003]字符串折叠 区间DP
1090: [SCOI2003]字符串折叠 Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/p ...
- 【BZOJ1090】[SCOI2003]字符串折叠(动态规划)
[BZOJ1090][SCOI2003]字符串折叠(动态规划) 题面 BZOJ 洛谷 题解 区间\(dp\).设\(f[i][j]\)表示压缩\([i,j]\)区间的最小长度.显然可以枚举端点转移.再 ...
- 【bzoj1090】 [SCOI2003]字符串折叠
[bzoj1090] [SCOI2003]字符串折叠 2014年3月9日3,1140 Description 折叠的定义如下: 1. 一个字符串可以看成它自身的折叠.记作S S 2. X(S)是X ...
- BZOJ1090: [SCOI2003]字符串折叠
区间dp. 一种是分段dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]); 一种是这一段可以缩写dp[i][j]=min(dp[i][j],dp[i][l]+2+ca ...
- [luoguP4302] [SCOI2003] 字符串折叠 解题报告(区间DP)
题目链接:https://www.luogu.org/problemnew/show/P4302 题解: 我们考虑折叠一个区间里的字符串,怎么样才是最优的 1.把这个区间分成几部分分别折叠 2.把这个 ...
- [bzoj1090][SCOI2003]字符串折叠_区间dp
字符串折叠 bzoj-1090 SCOI-2003 题目大意:我说不明白...链接 注释:自己看 想法:动态规划 状态:dp[i][j]表示从第i个字符到第j个字符折叠后的最短长度. 转移:dp[l] ...
随机推荐
- 产品需求说明书PRD模版
<软件自动化测试开发>出版了 XXX产品需求说明书 [版本号:V+数字] 编 制: 日 期: 评 审: 日 期: 批 准: 日 期: ...
- SIM卡的消失会让运营商们恐慌吗?
中国移动.联通.电信三大运营商原本高高在上,每天乐滋滋地数钱数到手抽筋,但近年来移动互联网的快速普及,让运营商的制霸状态不复存在.成为众多互联网公司的"流量通道",语音.短信等业 ...
- Ubuntu 14.04 下NFS安装配置
1.执行命令:sudo apt-get install nfs-kernel-server; 2.执行命令:mkdir /home/jack/nfs-share 建立一个nfs服务的专有的文件夹; 3 ...
- K8S实战-构建Django项目-03-使用共享存储
上篇博文,发布之后,正好跟着双十一,不知道大家剁手了没~~.好啦,言归正传先声明一下,每周1,3,5更新教程,大家如果想要了解更多的教程可以重温一下之前的教程或者,关注崔格拉斯 公众号,大家想要源码的 ...
- 使用TensorFlow训练自己的语音识别AI
这次来训练一个基于CNN的语音识别模型.训练完成后,我们将尝试将此模型用于Hotword detection. 人类是怎样听懂一句话的呢?以汉语为例,当听到"wo shi"的录音时 ...
- bootstrap-select and selectpicker 修改下拉框的宽度或者下方留白
bootstrap-select and selectpicker 修改下拉框的宽度或者下方留白 $("#sel_userName").selectpicker({ "w ...
- jstack的使用
一.概述 有些时候我们需要查看下jvm中的线程执行情况,比如,发现服务器的CPU的负载突然增高了.出现了死锁.死循环等,我们该如何分析呢? 由于程序是正常运行的,没有任何的输出,从日志方面也看不出什么 ...
- 第一篇:注册中心Eureka
1.什么是Eureka,有什么用? Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service Registry和Service Discovery实现.也是spri ...
- [面试专题]前端需要知道的web安全知识
前端需要知道的web安全知识 标签(空格分隔): 未分类 安全 [Doc] Crypto (加密) [Doc] TLS/SSL [Doc] HTTPS [Point] XSS [Point] CSRF ...
- CSS盒子模型以及外边框合并的问题
盒子模型 我们把布局里面的所有东西都可以想象成一个盒子,盒子里面又装着小盒子,小盒子里面又装着小小盒子......所以布局的万物基于盒子.即使一个小小的元素p,也可以把它抽象成为一个盒子.你现在心里有 ...