题解 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] ...
随机推荐
- JavaScript值类型和引用类型有哪些
JavaScript值类型和引用类型有哪些 (1)值类型:数值.布尔值.null.undefined. (2)引用类型:对象.数组.函数.
- 推拿O2O 想说爱你还不容易
想说爱你还不容易" title="推拿O2O 想说爱你还不容易"> <屌丝男士>第四季最后一集里,乔杉终于圆了"大保健"的梦想,可惜 ...
- 关于PHPExcel的一些资料
下面是总结的几个使用方法 include 'PHPExcel.php'; include 'PHPExcel/Writer/Excel2007.php'; //或者include 'PHPExcel/ ...
- 正则表达式之RegExp对象
1.定义 RegExp对象是原生JavaScript中表示正则表达式的对象:是正则表达式(regular expression)的简写.有一点需要明确知道,正则表达式只能对字符串进行操作. 2.语法 ...
- Docker实战之MySQL主从复制
前言 曾几何时,看着高大上的架构和各位前辈高超的炫技,有没有怦然心动,也想一窥究竟?每当面试的时候,拿着单应用的架构,吹着分库分表的牛X,有没有心里慌的一批? 其实很多时候,我们所缺少的只是对高大上的 ...
- XML的相关基础知识分享(二)
前面我们讲了一下XML相关的基础知识(一),下面我们在加深一下,看一下XML高级方面. 一.命名空间 1.命名冲突 XML命名空间提供避免元素冲突的方法. 命名冲突:在XML中,元素名称是由开发者定义 ...
- 7-35 jmu-python-求三角形面积及周长 (10 分)
输入的三角形的三条边a.b.c,计算并输出面积和周长.假设输入三角形三边是合法整形数据. 三角形面积计算公式: ,其中s=(a+b+c)/2. import math #导入math库 math.s ...
- Yoshino: 一个基于React的可定制化的PC组件库
Github: https://github.com/Yoshino-UI... Docs: https://yoshino-ui.github.io/#/ Cli-Tool: https://git ...
- 痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU启动那些事(6.1)- FlexSPI NOR连接方式大全(RT600)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是恩智浦i.MX RT600的FlexSPI NOR启动的连接方式. 痞子衡前段时间一鼓作气写完了三篇关于i.MXRT1xxx系列Flex ...
- JAVA有关位运算的全套梳理
一.在计算机中数据是如何进行计算的? 1.1:java中的byte型数据取值范围 我们最开始学习java的时候知道,byte类型的数据占了8个bit位,每个位上或0或1,左边第一位表示符号位,符号位如 ...