题解 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] ...
随机推荐
- 未来京东真能成为中国第一大B2C电商平台吗?
2月10日,京东集团在北京举行2017年"科技引领未来"开年年会.在本届年会上,京东宣布全面向技术转型.京东集团CEO刘强东正式对外公布未来12年的战略:在以人工智能为 ...
- Azure Devops测试管理(上)
因为最近测试人员合并到我这边开发组,对于如何能更好管理测试流程和测试与开发能更高效的完成任务,通俗的说如何能更敏捷,深入思考,然后就开始琢磨起TFS(也称之为VSTS/Azure Devops,因为我 ...
- Flutter Widgets 之 ListWheelScrollView
注意:无特殊说明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 基础用法 在展示大量数据的时候我们第一会想到使用ListV ...
- iNeuOS工业互联平台,.NETCore开发的视频服务组件iNeuVideo,RTSP转WebSocket
目 录 1. 概述... 2 2. 将来集成到iNeuOS平台演示... 3 3. iNeuVideo结构... 3 4. iNeuVideo部署及 ...
- Ajax&Json案例
案例: * 校验用户名是否存在 1. 服务器响应的数据,在客户端使用时,要想当做json数据格式使用.有两种解决方案: 1. $.get(type):将最后一个参数type指定为"json& ...
- KEMET推出新的多层陶瓷电容器,用于市电供电的应用
前言:2019年12月2日,全球领先的电子组件供应商KEMET公司 (“ KEMET”或“公司”)推出了一系列新的表面安装设备(SMD)安全认证的多层陶瓷电容器(MLCC),用于市电供电的应用.与现有 ...
- Java设计模式之结构模式
一.外观模式 分析:外观模式是为子系统的一组接口提供一个统一的界面,数据库JDBC连接应用就是外观模式的一个典型例子,特点:降低系统的复杂度,增加灵活性.结果:代码示例: public class D ...
- Go语言中的数据类型转换
在go语言中,不同类型的变量之间赋值需要显示转换. 语法:T t=T(e) //将i转换为float类型 var j float32=float32(i) 基本数据类型转string 方法1:fmt. ...
- 使用R进行空间自相关检验
「全局溢出」当一个区域的特征变化影响到所有区域的结果时,就会产生全局溢出效应.这甚至适用于区域本身,因为影响可以传递到邻居并返回到自己的区域(反馈).具体来说,全球溢出效应影响到邻居.邻居到邻居.邻居 ...
- 2019-2020-2 20175226 王鹏雲 网络对抗技术 Exp2 后门原理与实践
2019-2020-2 20175226 王鹏雲 网络对抗技术 Exp2 后门原理与实践 实验内容 使用netcat获取主机操作Shell,cron启动: 使用socat获取主机操作Shell, 任务 ...