C++日常刷题积累
- 今日刷题汇总 - day012
- 1、删除公共字符
- 1.1、题目
- 1.2、思路
- 1.3、程序实现 -- 蛮力法
- 1.4、程序实现 -- 哈希
- 2、两个链表的第一个公共结点
- 2.1、题目
- 2.2、思路
- 2.3、程序实现 -- 对齐比对法
- 2.4、程序实现 -- 公共端点路程法
- 3、mari和shiny
- 3.1、题目
- 3.2、思路
- 3.3、程序实现
- 3.4、程序实现 -- 空间优化
- 4、题目链接
今日刷题汇总 - day012
1、删除公共字符
1.1、题目
1.2、思路
读完题知道,输入两个字符串str1和str2,然后去除str1中与str2相关的字符,输出即可。那么蛮力法思路,可以遍历str1,再套一层遍历str2,判断字符是否相同,不相同则尾插到retstr中,最后输出即可。还可以进行优化,可以采用hash表标记str2中的字符,然后就只需要遍历一遍str1字符,如果在hash中已经存在,那么就是相同字符,不存在那么就尾插到retstr中即可,最后输出即可。那么,接下来就是程序实现。
1.3、程序实现 – 蛮力法
根据思路的蛮力法,挨着挨着遍历比较即可。注意控制边界和尾插条件即可。可以简化思路,使用标志位flag也可以处理。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1;
string str2;
getline(cin, str1);
getline(cin, str2);
string retstr;
size_t len1 = str1.size();
size_t len2 = str2.size();
for(int i = 0;i < len1; i++)
{
for(int j = 0;j < len2; j++)
{
if (str1[i] == str2[j])
break;
else if((str1[i] != str2[j]) && (j == len2-1))
{
retstr += str1[i];
}
}
}
cout << retstr << endl;
return 0;
}
利用标志位简化逻辑控制判断,但注意标志位每一次遍历前都置0.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1;
string str2;
getline(cin, str1);
getline(cin, str2);
string retstr;
size_t len1 = str1.size();
size_t len2 = str2.size();
int flag = 0;
for(int i = 0;i < len1; i++)
{
flag = 0;
for(int j = 0;j < len2; j++)
{
if(str1[i] == str2[j])
{
flag = 1;
break;
}
}
if(!flag)
retstr += str1[i];
}
cout << retstr << endl;
return 0;
}
1.4、程序实现 – 哈希
利用哈希表标记一下str2的字符,只需要遍历一遍str1如果不存在就尾插到retstr最后输出即可。
另外,由于此提示ACM题型,可以单独输出每个字符,不用开辟retstr额外的空间。那么两个都写一写吧。
#include <iostream>
#include <string>
using namespace std;
int main()
{
bool hash[300] = { false };
string str1;
string str2;
string retstr;
getline(cin,str1);
getline(cin,str2);
size_t len1 = str1.size();
size_t len2 = str2.size();
for(int i = 0;i < len2; i++)
hash[str2[i]] = true;
for(int i = 0;i < len1;i++)
{
if(!hash[str1[i]])
retstr += str1[i];
}
cout << retstr << endl;
return 0;
}
也可以不用尾插,直接用范围for判断一个字符就直接输出单个字符,直到结束遍历即可。
#include <iostream>
#include <string>
using namespace std;
int main()
{
bool hash[300] = { false };
string str1;
string str2;
getline(cin,str1);
getline(cin,str2);
size_t len1 = str1.size();
size_t len2 = str2.size();
for(auto ch : str2)
hash[ch] = true;
for(auto ch : str1)
{
if(!hash[ch])
cout << ch;
}
return 0;
}
2、两个链表的第一个公共结点
2.1、题目
2.2、思路
读完题,知道是之前学数据结构链表章节做过的类似题目,这里要求两个单链表的第一个相遇的公共端点,有则返回该端点,无则返回NULL即可。根据题目和示例分析,输入分为三部分,可以想到两种解题思路。借助之前数据结构的思路思考得出第一种解题思路,这里为了方便描述,个人取名为“对齐比对法”,意思就是将两个链表对齐,将较长的链表去除多余的非公共部分,达到与第二个链表对齐的效果,再一起利用cur1和cur2同时遍历两个链表,当cue1 和 cur2 且 cur1->val 与 cur2->val都指向且等于同一个结点时,那么这个结点就是第一个公共结点。
第二种思路,学习大佬的解题思路秒不可言,这里我称为“公共端点路程法”,我理解的思路也是需要利用两个指针,分别从两个链表同时遍历,各自的非公共部分 + 公共部分 + 对方的非公共部分,遍历结束的位置就是第一个公共端点的位置。
综上所述,两个思路都挺简单的,但是为了更清晰的理解,画个图:
根据思路分析,方法一需要解决以下几个问题:
(1)、如何去除较长单链表的非公共部分;
(2)、如何确定两个指针cur1和cur2对齐;
(3)、能否处理题目示例2中,公共端点为空的情况?
思考后解决思路:
(1)、可以这样理解,先用cur1和cur2指针遍历一遍各自的单链表,当一个指针如cur2遍历完指向NULL时,暂停cur1的遍历,此时利用一个计数器如count++,再继续执行cur1的遍历,直到cur1遍历到NULL结束。那么此时就可以得出两个结果,一个是先遍历完的是较短的单链表,第二个是后遍历完的链表的count就是它们之间的差值,把差值拿到较长链表的开头进行去除,就能实现去除多余的非公共部分结点。
(2)、基于(1)的基础上,置去除结点之后的这个结点为cur1与较短单链表的开头结点为cur2即可对齐。
(3)、其实不管是方法一“对齐比对法”还是方法二“公共端点路程法”都已经兼容了,处理为空的情况。因为遍历完本身没有公共点就是为NULL,那么自然而然就返回NULL。可以画个图,简单看一下就很清楚了哈。这里我就不详细演示了:
那么接下来,就是程序实现。
2.3、程序实现 – 对齐比对法
综上所述,总结后基本分为以下几个步骤:
(1)、计算两个链表的长度和差值;
(2)、头删较长单链表;
(3)、同步两个链表的指针,同时遍历;
思路写得很详细了,是好理解简单的,程序就细心一点就可以了。值得再提一提的就是头删后更新一下cur1和cur2以及执行while遍历的条件即可。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2)
{
int count = 0;
int len1 = 0;
int len2 = 0;
ListNode* cur1 = pHead1;
ListNode* cur2 = pHead2;
//计算差值
while(cur1)
{
len1++;
cur1 = cur1->next;
}
while(cur2)
{
len2++;
cur2 = cur2->next;
}
count = abs(len1 - len2);
//头删较长单链表
if(len1 >= len2)
{
while(count--)
{
pHead1 = pHead1->next;
}
//同步两个链表的指针,同时遍历
cur1 = pHead1;
cur2 = pHead2;
while(cur1 != cur2 && cur1->val != cur2->val)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
}
else
{
while(count--)
{
pHead2 = pHead2->next;
}
//同步两个链表的指针,同时遍历
cur1 = pHead1;
cur2 = pHead2;
while(cur1 != cur2 && cur1->val != cur2->val)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
}
return cur1;
}
};
2.4、程序实现 – 公共端点路程法
接着,总结思路分析,主要可以分为以下几个步骤:
(1)、定义工作指针,分别指向两个单链表;
(2)、严格按照思路分析的顺序,遍历单链表;
(3)、遍历结束就是结果的公共端点。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
ListNode* cur1 = pHead1;
ListNode* cur2 = pHead2;
while(cur1 != cur2)
{
cur1 = cur1 != NULL ? cur1->next : pHead1;
cur2 = cur2 != NULL ? cur2->next : pHead2;
}
return cur1;
}
};
3、mari和shiny
3.1、题目
3.2、思路
读完题,知道让求一段仅由小写字母组成的字符串中,有多少个子序列"shy"。其中,根据示例得知,这段字符串的“‘s’,‘h’,‘y’”不一定是连续的,可自由组成子序列"shy",最后求最后能拼接多少个子序列"shy"。那么,分析示例和思考,每一个’s’ 、‘h’、'y’都可以不重复的任意拼接。可知"shy"由"sh"和‘y’决定,而"sh’由‘s’和‘h’决定。那么结合多状态的线性dp动态规划的思路或者分治法的思想,划分为若干个子问题求解。那么继续思考dp的状态表示和状态转移方程,为了方便理解,画个图:
接着考虑舒适化各个dp[i]的第一个元素,同样需要判断是否就是‘s’ 或 ‘h’ 或 ‘y’,是则置1否则置0完成初始化即可。接下来,就是程序实现。
3.3、程序实现
首先,根据题目要求写好输入,且注意范围这里使用long long防止越界,然后就可以定义多状态的dp数组,即s(len, 0), h(len, 0), y(len, 0)即可,然后初始化dp的第一个元素,然后就是分析的状态转移方程思路,遍历判断各个字符再累加个数,最终得到y[len-1]就是长度为n最多子序列"shy"的个数输出即可。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
long long n;
cin >> n;
string str;
cin >> str;
size_t len = str.size();
vector<long long> s(len, 0), h(len, 0), y(len, 0);
// 初始化第一个元素
s[0] = (str[0] == 's') ? 1 : 0;
h[0] = (str[0] == 'h' && s[0] > 0) ? 1 : 0;
y[0] = (str[0] == 'y' && h[0] > 0) ? 1 : 0;
for (size_t i = 1; i < len; i++)
{
if (str[i] == 's')
{
s[i] = s[i-1] + 1;
}
else
{
s[i] = s[i-1];
}
if (str[i] == 'h')
{
h[i] = h[i-1] + s[i-1];
}
else
{
h[i] = h[i-1];
}
if (str[i] == 'y')
{
y[i] = y[i-1] + h[i-1];
}
else
{
y[i] = y[i-1];
}
}
cout << y[len-1] << endl;
return 0;
}
3.4、程序实现 – 空间优化
进一步思考,发现可以控制三个变量代替各个dp来表示’s’ 和 ‘h’ 以及 'y’的个数,实现空间的优化。并且这种写法兼容了[0,i-1]区间内str[i] != ‘s’ 或 ‘h’ 或 'y’的情况,达成此题的最优解法。
#include <iostream>
#include <string>
using namespace std;
int main()
{
int n = 0;
cin >> n;
string str;
cin >> str;
long long s = 0, h = 0, y = 0;
for(int i = 0;i < n; i++)
{
char ch = str[i];
if(ch == 's')
s++;
else if(ch == 'h')
h += s;
else if(ch == 'y')
y += h;
}
cout << y << endl;
return 0;
}
4、题目链接
删除公共字符
两个链表的第一个公共结点
mari和shiny