昨天有两场大创答辩,没来得及做算法,所以晚一天发布。
DAY9
ACWING区间合并
- #include<iostream>
- #include<vector>
- #include<algorithm>
- using namespace std;
- typedef pair<int,int> PII;
- const int N=100010;
- int n;
- //用pair把区间的左右端点存到vector里去。first\second
- vector<PII> segs;
- void merge(vector<PII> &segs)
- {
- vector<PII> res;//定义答案,存合并之后的结果
- //所有区间按左端点排序。pair排序优先以左端点排序
- sort(segs.begin(),segs.end());
- //取边界,负无穷和负无穷,具体值见题给数据范围。表示当前维护区间
- int st=-2e9,ed=-2e9;
- for(auto seg:segs) //枚举。也就是扫描区间
- if(ed<seg.first)//无交集,无法合并
- {
- if(st!=-2e9) res.push_back({st,ed});//将当前维护的区间加入到答案中去
- st=seg.first,ed=seg.second;//更新维护区间
- }
- //有交集,更新,max勿漏
- else ed=max(ed,seg.second);
- //把最后的区间加入到答案中去
- //防止空区间加入到答案中去
- if(st!=-2e9) res.push_back({st,ed});
- segs=res;
- }
- int main()
- {
- cin>>n;
- for(int i=0;i<n;i++)
- {
- int l,r;
- cin>>l>>r;
- segs.push_back({l,r});
- }
- //区间合并的模板
- merge(segs);
- cout<<segs.size()<<endl;
- return 0;
- }
28实现strStr():找出字符串中第一个匹配项的下标,简单
- class Solution {
- public:
- int strStr(string haystack, string needle) {
- //这里和学习的模板不一样,传入的字符串下标从0开始,怎么操作呢?新开字符串并从下标1开始赋值就好了。空模式串,则return 0;记住:如果报错数组下标溢出之类的,记得检查for的i上界有没有写错。
- int l1=haystack.size(),l2=needle.size();
- if(l2==0) return 0;
- string h[l1+2],n[l2+2];
- int ne[10010];
- ne[1]=0;
- for(int i=1;i<=l1;i++) h[i]=haystack[i-1];
- for(int i=1;i<=l2;i++) n[i]=needle[i-1];
- for(int i=2,j=0;i<=l2;i++)
- {
- while(j!=0 && n[i]!=n[j+1]) j=ne[j];
- if(n[i]==n[j+1]) j++;
- ne[i]=j;
- }
- for(int i=1,j=0;i<=l1;i++)
- {
- while(j!=0 &&h[i]!=n[j+1]) j=ne[j];
- if(h[i]==n[j+1]) j++;
- if(j==l2)
- {
- return i-l2;
- }
- }
- return -1;
- }
- };
很好!下一题也加油!
459重复的子字符串,简单
没有想出暴力法怎么做,更别提优化了。接下来在代码随想录上学一学暴力法:
让COZE读代码随想录的暴力法,然后给出代码:
- 暴力法思路非常好,很值得学习:
Prompt:
Leetcode459.重复的子字符串:根据思路给出实现的代码:思路:暴力的解法, 就是一个for循环获取 子串的终止位置, 然后判断子串是否能重复构成字符串,又嵌套一个for循环,所以是O(n^2)的时间复杂度。有的同学可以想,怎么一个for循环就可以获取子串吗? 至少得一个for获取子串起始位置,一个for获取子串结束位置吧。其实我们只需要判断,以第一个字母为开始的子串就可以,所以一个for循环获取子串的终止位置就行了。 而且遍历的时候 都不用遍历结束,只需要遍历到中间位置,因为子串结束位置大于中间位置的话,一定不能重复组成字符串。
Answer:
根据你的思路,我们只需要一个for循环来获取子串的结束位置,并在遍历时只需要遍历到原字符串的中间位置。下面是一种可能的实现方式:
i 是子串的结尾的后一个指针。即:i是子串长度
- class Solution {
- public:
- bool repeatedSubstringPattern(string s) {
- int n = s.size(); // 获取字符串的长度
- // 遍历所有可能的子串长度,从1开始到字符串长度的一半
- for(int i = 1; i <= n / 2; ++i) {
- // 如果字符串的长度能被子串长度整除,那么这个子串就可能是重复的子串
- if(n % i == 0) {
- // 假设这个子串就是重复的子串。精髓!也是bool设置的思想,我不太熟,不妨这样思考来安放bool
- bool match = true;
- // 遍历字符串,检查是否所有的字符都与子串的对应字符相等
- for(int j = i; j < n; ++j) {
- // 如果找到了一个字符与子串的对应字符不等,那么这个子串就不可能是重复的子串
- if(s[j] != s[j-i]) {
- match = false;
- break;
- }
- }
- // 如果所有的字符都与子串的对应字符相等,那么我们找到了一个重复的子串,返回true
- if(match) {
- return true;
- }
- }
- }
- // 如果我们遍历完所有可能的子串都没有找到重复的子串,那么返回false
- return false;
- }
- };
这段代码首先遍历所有可能的子串长度,如果原字符串的长度能被子串的长度整除,那么这个子串就有可能是重复的子串。然后,对每个可能的子串,我们检查它是否能通过重复得到原字符串。如果能,我们就返回 true;否则我们继续检查下一个可能的子串。如果遍历完所有可能的子串都没有找到可以通过重复得到原字符串的子串,我们就返回 false。
注意,这段代码的时间复杂度为 O(n^2),在最坏的情况下,它需要检查 n/2 个可能的子串,每个子串需要 O(n) 的时间来进行比较。虽然这个暴力解法在处理大规模输入时可能会比较慢,但它仍然可以解决这个问题。如果你需要一个更高效的解法,你可能需要使用一些更复杂的技术,例如 KMP 算法或 Rabin-Karp 算法。
问:s[j-i] 是原字符串中的第 j-i 个字符,也就是子串中应该与 s[j] 对应的字符。为什么?详细解释
答:j 是我们当前正在检查的字符在原字符串 s 中的位置,i 是我们假设的重复子串的长度。假设我们的子串是重复的,那么在原字符串 s 中,每一个位置 j 的字符应该和它前面 i 个位置的字符是一样的,因为它们都是来自于同一个位置在重复的子串中。
举个例子,假设我们有一个字符串 "abcabcabc",我们的 i 是3,意味着我们认为可能的重复子串是 "abc"。当我们在检查原字符串的第4个字符(也就是第二个 "a")的时候,我们期待它和它前面 i 个位置的字符(也就是第一个 "a")是一样的,因为它们都应该是子串 "abc" 中的第一个字符。这就是为什么我们会比较 s[j](也就是原字符串的第 j 个字符)和 s[j-i](也就是在原字符串中对应到重复子串的同一个位置的字符)。
所以,s[j-i] 是原字符串中的第 j-i 个字符,也就是子串中应该与 s[j] 对应的字符。如果它们不相等,那么这个子串就不能通过重复得到原字符串。
取余复杂了,减去周期就好。自己写一遍,看能不能过:终于理解了,磕磕绊绊过了。终于知道怎么使用bool了,收获颇丰。
- class Solution {
- public:
- bool repeatedSubstringPattern(string s) {
- int n = s.size();
- for (int i = 1; i <= n / 2; i++) {
- if (n % i == 0) {
- bool res = true; // 假设它是!!
- for (int j = i; j < n; ++j) {
- if (s[j] != s[j - i]) {
- res = false; // 假设错误,下一个
- break; // 此串不是答案,试试下一个
- }
- }
- if (res) //假设通过了检验return吧!
- return true;
- }
- }
- return false;
- }
- };
- 移动匹配
学会了string+string 的操作,竟然能实现字符串拼接。
学会了:std::string::npos 就是一个表示“没有找到”的特殊值。
思路很简单,自己写一遍:
- class Solution {
- public:
- bool repeatedSubstringPattern(string s) {
- string t=s+s;
- t.erase(t.begin());
- t.erase(t.end()-1);
- if(t.find(s)!=std::string::npos) return true;
- return false;
- }
- };
- KMP
还能用KMP?看看怎么个事
KMP:在一个串中查找是否出现过另一个子串
下标从1开始(图片来自王道-数据结构考研课程-BILIBILI):
唉算了,别折磨自己,精通两个方法足够了。
马上发布DAY10!