牛客网编程题-超级子串
2016-07-15 20:11
204 查看
题目如下:
分析:
刚开始看到这个题的时候就懵了,完全没有思路,后来经过分析有如下想法。
首先,这道题和我们的求字符串的最长无重复子串不同,因为最长无重复子串要求满足条件的字符串区间内没有重复的字符,但是这个题目满足条件的字符串区间可以有重复的子串,如图有如下情况:
HGSFKGHFMNOPQGABCDEGHIK
我们看到红色部分是最佳的满足条件的子串,尽管这个子串含有重复的字符,因为这个题目满足这样的逻辑:
假设有字符串如下
a1a2a3a4a5a6a4a8a9a10
假设a1到a6之间没有重复的字符,但是接下来有个重复的字符a4,这个时候如果把a4加入到已有的未重复字符串中且当前子串的总长度为6,但是这个时候我们如果从新a4开始从新计算的话得到值是4(即a4a8a9a10),那么和前面的6比较得到满足条件的子串是a1a2a3a4a5a6。但事实是这样的吗?
如果我们将a4继续累加,虽然总长度增加1变成7,但是【a1a2a3a4a5a6a4】之间的无重复字符的个数确变成7-2=5个,好像比之前说过的六个还要少,这个好像不行?但是当我们继续累加直到整个字符串结束,得到的结果是8.这个子串才是最符合条件的。
到这里我们不由得想到了动态规划算法,基本思想是计算每两个字符之间(包含两个端点字符)构成子串的完美度,然后将他们记录在一张n×n的表格中
具体代码如下:
class mysolution{
public:
pair<int, int>TheMostPerfectSubstring1(string s) {//动态规划法
int len = 0,j,now=0,size=s.size();//具有最大完美度的字符子串中无重复字符的个数
pair<int, int>result;
vector<int>t(size,0);
vector<vector<int>>p;
for (int i = 0; i < size; i++)p.push_back(t);
for (int i = 0; i < size; i++)p[i][i] = 1;
int arr[26] = { 0 }, tmp[26];//统计到目前为止出现的次数
for (int i = 0; i < size; i++){//i控制列
if (arr[s[i] - 65] == 0)now++;
else if (arr[s[i] - 65] == 1)now--;
else{};//大于或等于2的时候不对now操作
p[0][i] = now;
if (p[0][i]>len){
len = p[0][i];
result.first = 0;
result.second = i;
}
arr[s[i] - 65]++;
memcpy(tmp,arr,26*sizeof(int));
for(j = 0; j<i-1;j++){//j控制行,当j等于k,表示除去k
int temp = --tmp[s[j]-65];
if (temp == 0)p[j+1][i] = p[j][i] - 1;
else if (temp == 1)p[j+1][i] = p[j][i] + 1;
else p[j+1][i] = p[j][i];
if (p[j+1][i]>len){
len = p[j+1][i];
result.first = j+1;
result.second = i;
}
}
}
return result;
}
};
思想描述:
使用二维容器是为了方便调试p中的内容,在上面的代码中我们主要利用了两点:
1.在p的第一行中,我们保存了截至到目前为止的无重复字符的个数,同时用arr数组来保存截至当前每个字符出现的个数;
2.在求解的过程中我们利用第一行的(截至到目前为止无重复出现字符的个数)值来反向推倒,假设有如下的例子:
IAMAGO ODBOYTA MGDBSKH
将中间的空格处理之后就是
IAMAGOODBOYTAMGDBSKH
那我们的表格的第一行就是:
那么字母下面的数字就是代表截至目前为止无重复字符的个数,现在我们得到了p的第一行,那么我们怎样得到p的第二行呢?过程是这样的:
以如下分支为例:
我们知道p[1][4]=2,代表从第1个字符到第4个字符的无重复字符个数为2,那么p[2][4]怎么求,理论上很简单,就是要去掉第一字符,第1个字符有如下几种情况:
A.第1个字符只出现1次
那么我们将这个字符拿掉的时候,p[2][4]无重复字符的数量肯定要-1;
B.第1个字符刚好出现2次
如果刚好出现两次,那我们把第1个拿掉的时候,这个字符剩下出现的次数变成1(代表无重复出现了),那么无重复字符的个数要+1;
C.第1个字符出现3次以上
如果该字符,那么该字符的剩余个数大于等于2,这个时候对无重复字符的个数毫无影响(这个逻辑要想清楚);
那么这个过程的动态规划过程就是:
p[j+1][i] = p[j][i] - 1;
p[j+1][i] = p[j][i] + 1;
p[j+1][i] = p[j][i];
P[j+1][i]表示从下标j+1开始到下标i截至字符出现的无重复字符的个数,P[j][i]表示从下标j开始到下标i截至字符出现的无重复字符的个数,整个算法的精髓就在两点:
1.首先求出第1行中截至到目前为止无重复字符出现的个数以及截至当前每个字符出现的次数;
2.利用上述的动态规划递推式至上而下将p中的每一行数据填满,这样,从任意点i到j之间的无重复出现字符的个数就全部记录在二维容器p中;
测试数据:
测试结果:
分析:
刚开始看到这个题的时候就懵了,完全没有思路,后来经过分析有如下想法。
首先,这道题和我们的求字符串的最长无重复子串不同,因为最长无重复子串要求满足条件的字符串区间内没有重复的字符,但是这个题目满足条件的字符串区间可以有重复的子串,如图有如下情况:
HGSFKGHFMNOPQGABCDEGHIK
我们看到红色部分是最佳的满足条件的子串,尽管这个子串含有重复的字符,因为这个题目满足这样的逻辑:
假设有字符串如下
a1a2a3a4a5a6a4a8a9a10
假设a1到a6之间没有重复的字符,但是接下来有个重复的字符a4,这个时候如果把a4加入到已有的未重复字符串中且当前子串的总长度为6,但是这个时候我们如果从新a4开始从新计算的话得到值是4(即a4a8a9a10),那么和前面的6比较得到满足条件的子串是a1a2a3a4a5a6。但事实是这样的吗?
如果我们将a4继续累加,虽然总长度增加1变成7,但是【a1a2a3a4a5a6a4】之间的无重复字符的个数确变成7-2=5个,好像比之前说过的六个还要少,这个好像不行?但是当我们继续累加直到整个字符串结束,得到的结果是8.这个子串才是最符合条件的。
到这里我们不由得想到了动态规划算法,基本思想是计算每两个字符之间(包含两个端点字符)构成子串的完美度,然后将他们记录在一张n×n的表格中
具体代码如下:
class mysolution{
public:
pair<int, int>TheMostPerfectSubstring1(string s) {//动态规划法
int len = 0,j,now=0,size=s.size();//具有最大完美度的字符子串中无重复字符的个数
pair<int, int>result;
vector<int>t(size,0);
vector<vector<int>>p;
for (int i = 0; i < size; i++)p.push_back(t);
for (int i = 0; i < size; i++)p[i][i] = 1;
int arr[26] = { 0 }, tmp[26];//统计到目前为止出现的次数
for (int i = 0; i < size; i++){//i控制列
if (arr[s[i] - 65] == 0)now++;
else if (arr[s[i] - 65] == 1)now--;
else{};//大于或等于2的时候不对now操作
p[0][i] = now;
if (p[0][i]>len){
len = p[0][i];
result.first = 0;
result.second = i;
}
arr[s[i] - 65]++;
memcpy(tmp,arr,26*sizeof(int));
for(j = 0; j<i-1;j++){//j控制行,当j等于k,表示除去k
int temp = --tmp[s[j]-65];
if (temp == 0)p[j+1][i] = p[j][i] - 1;
else if (temp == 1)p[j+1][i] = p[j][i] + 1;
else p[j+1][i] = p[j][i];
if (p[j+1][i]>len){
len = p[j+1][i];
result.first = j+1;
result.second = i;
}
}
}
return result;
}
};
思想描述:
使用二维容器是为了方便调试p中的内容,在上面的代码中我们主要利用了两点:
1.在p的第一行中,我们保存了截至到目前为止的无重复字符的个数,同时用arr数组来保存截至当前每个字符出现的个数;
2.在求解的过程中我们利用第一行的(截至到目前为止无重复出现字符的个数)值来反向推倒,假设有如下的例子:
IAMAGO ODBOYTA MGDBSKH
将中间的空格处理之后就是
IAMAGOODBOYTAMGDBSKH
那我们的表格的第一行就是:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
I | A | M | A | G | O | O | D | B | O | Y | T | A | M | G | D | B | S | K | H |
1 | 2 | 3 | 2 | 3 | 4 | 3 | 4 | 5 | 5 | 6 | 7 | 7 | 6 | 5 | 4 | 3 | 4 | 5 | 6 |
那么字母下面的数字就是代表截至目前为止无重复字符的个数,现在我们得到了p的第一行,那么我们怎样得到p的第二行呢?过程是这样的:
以如下分支为例:
1 | 2 | 3 | 4 |
I | A | M | A |
1 | 2 | 3 | 2 |
我们知道p[1][4]=2,代表从第1个字符到第4个字符的无重复字符个数为2,那么p[2][4]怎么求,理论上很简单,就是要去掉第一字符,第1个字符有如下几种情况:
A.第1个字符只出现1次
那么我们将这个字符拿掉的时候,p[2][4]无重复字符的数量肯定要-1;
B.第1个字符刚好出现2次
如果刚好出现两次,那我们把第1个拿掉的时候,这个字符剩下出现的次数变成1(代表无重复出现了),那么无重复字符的个数要+1;
C.第1个字符出现3次以上
如果该字符,那么该字符的剩余个数大于等于2,这个时候对无重复字符的个数毫无影响(这个逻辑要想清楚);
那么这个过程的动态规划过程就是:
p[j+1][i] = p[j][i] - 1;
p[j+1][i] = p[j][i] + 1;
p[j+1][i] = p[j][i];
P[j+1][i]表示从下标j+1开始到下标i截至字符出现的无重复字符的个数,P[j][i]表示从下标j开始到下标i截至字符出现的无重复字符的个数,整个算法的精髓就在两点:
1.首先求出第1行中截至到目前为止无重复字符出现的个数以及截至当前每个字符出现的次数;
2.利用上述的动态规划递推式至上而下将p中的每一行数据填满,这样,从任意点i到j之间的无重复出现字符的个数就全部记录在二维容器p中;
测试数据:
测试结果:
相关文章推荐
- Java 命令行运行参数大全
- 深入分析Java I/O 工作机制
- 【Qt】标准输入对话框
- Java学习笔记之深入理解关键字super
- MATLAB学习记录
- 流
- C++11 lambda表达式 实际上是lua的闭包方式
- spring MVC框架下 前台向java后台传送json字符串数据
- python challenge 7学习过程
- July 15th 模拟赛C T4 回家(莫名其妙【推荐】) Solution
- Java集合小结
- spring MVC框架下前台往java后台传送json数据
- python列表VS字典
- leetcode_c++:链表:Merge k Sorted Lists(023)
- java LinkedList 源码浅析
- c#——Winform DatagridView上显示下拉树
- C语言中常用的函数及注意事项
- **PHP** 文件操作
- org.springframework.dao.InvalidDataAccessResourceUsageException: could not insert:
- java去除字符串中的空格、回车、换行符、制表符的小例子