代码随想录算法训练营第36期DAY9

昨天有两场大创答辩,没来得及做算法,所以晚一天发布。

DAY9

ACWING区间合并

  1. #include<iostream>
  2. #include<vector>
  3. #include<algorithm>
  4. using namespace std;
  5. typedef pair<int,int> PII;
  6. const int N=100010;
  7. int n;
  8. //用pair把区间的左右端点存到vector里去。first\second
  9. vector<PII> segs;
  10. void merge(vector<PII> &segs)
  11. {
  12.     vector<PII> res;//定义答案,存合并之后的结果
  13.     //所有区间按左端点排序。pair排序优先以左端点排序
  14.     sort(segs.begin(),segs.end());
  15.     //取边界,负无穷和无穷,具体值见题给数据范围。表示当前维护区间
  16.     int st=-2e9,ed=-2e9;
  17.     for(auto seg:segs) //枚举。也就是扫描区间
  18.         if(ed<seg.first)//无交集,无法合并
  19.         {
  20.             if(st!=-2e9) res.push_back({st,ed});//将当前维护的区间加入到答案中去
  21.             st=seg.first,ed=seg.second;//更新维护区间
  22.         }
  23.         //有交集,更新,max勿漏
  24.         else ed=max(ed,seg.second);
  25.     //把最后的区间加入到答案中去   
  26.     //防止空区间加入到答案中去
  27.     if(st!=-2e9) res.push_back({st,ed});
  28.     segs=res;
  29. }
  30. int main()
  31. {
  32.     cin>>n;
  33.     for(int i=0;i<n;i++)
  34.     {
  35.         int l,r;
  36.         cin>>l>>r;
  37.         segs.push_back({l,r});
  38.     }
  39.     //区间合并的模板
  40.     merge(segs);
  41.     cout<<segs.size()<<endl;
  42.     return 0;
  43. }

28实现strStr():找出字符串中第一个匹配项的下标,简单

  1. class Solution {
  2. public:
  3.     int strStr(string haystack, string needle) {
  4.         //这里和学习的模板不一样,传入的字符串下标从0开始,怎么操作呢?新开字符串并从下标1开始赋值就好了。空模式串,则return 0;记住:如果报错数组下标溢出之类的,记得检查for的i上界有没有写错。
  5.         int l1=haystack.size(),l2=needle.size();
  6.         if(l2==0return 0;
  7.         string h[l1+2],n[l2+2];
  8.         int ne[10010];
  9.         ne[1]=0;
  10.         for(int i=1;i<=l1;i++) h[i]=haystack[i-1];
  11.         for(int i=1;i<=l2;i++) n[i]=needle[i-1];
  12.         for(int i=2,j=0;i<=l2;i++)
  13.         {
  14.             while(j!=0 && n[i]!=n[j+1]) j=ne[j];
  15.             if(n[i]==n[j+1]) j++;
  16.             ne[i]=j;
  17.         }
  18.         for(int i=1,j=0;i<=l1;i++)
  19.         {
  20.             while(j!=0 &&h[i]!=n[j+1]) j=ne[j];
  21.             if(h[i]==n[j+1]) j++;
  22.             if(j==l2)
  23.             {
  24.                 return i-l2;
  25.             }
  26.         }
  27.         return -1;
  28.     }
  29. };

很好!下一题也加油!

459重复的子字符串,简单

没有想出暴力法怎么做,更别提优化了。接下来在代码随想录上学一学暴力法:

让COZE读代码随想录的暴力法,然后给出代码:

  1. 暴力法思路非常好,很值得学习:

Prompt:

Leetcode459.重复的子字符串:根据思路给出实现的代码:思路:暴力的解法, 就是一个for循环获取 子串的终止位置, 然后判断子串是否能重复构成字符串,又嵌套一个for循环,所以是O(n^2)的时间复杂度。有的同学可以想,怎么一个for循环就可以获取子串吗? 至少得一个for获取子串起始位置,一个for获取子串结束位置吧。其实我们只需要判断,以第一个字母为开始的子串就可以,所以一个for循环获取子串的终止位置就行了。 而且遍历的时候 都不用遍历结束,只需要遍历到中间位置,因为子串结束位置大于中间位置的话,一定不能重复组成字符串。

Answer:

根据你的思路,我们只需要一个for循环来获取子串的结束位置并在遍历时只需要遍历到原字符串的中间位置。下面是一种可能的实现方式:

i 是子串的结尾的后一个指针。即:i是子串长度

  1. class Solution {
  2. public:
  3.     bool repeatedSubstringPattern(string s) {
  4.         int n = s.size(); // 获取字符串的长度
  5.         // 遍历所有可能的子串长度,从1开始到字符串长度的一半
  6.         for(int i = 1; i <= n / 2; ++i) {
  7.             // 如果字符串的长度能被子串长度整除,那么这个子串就可能是重复的子串
  8.             if(n % i == 0) {
  9.                 // 假设这个子串就是重复的子串。精髓!也是bool设置的思想,我不太熟,不妨这样思考来安放bool
  10.                 bool match = true;
  11.                 // 遍历字符串,检查是否所有的字符都与子串的对应字符相等
  12.                 for(int j = i; j < n; ++j) {
  13.                     // 如果找到了一个字符与子串的对应字符不等,那么这个子串就不可能是重复的子串
  14.                     if(s[j] != s[j-i]) {
  15.                         match = false;
  16.                         break;
  17.                     }
  18.                 }
  19.                 // 如果所有的字符都与子串的对应字符相等,那么我们找到了一个重复的子串,返回true
  20.                 if(match) {
  21.                     return true;
  22.                 }
  23.             }
  24.         }
  25.         // 如果我们遍历完所有可能的子串都没有找到重复的子串,那么返回false
  26.         return false;
  27.     }
  28. };

这段代码首先遍历所有可能的子串长度,如果原字符串的长度能被子串的长度整除,那么这个子串就有可能是重复的子串然后,对每个可能的子串,我们检查它是否能通过重复得到原字符串。如果能,我们就返回 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了,收获颇丰。

  1. class Solution {
  2. public:
  3.     bool repeatedSubstringPattern(string s) {
  4.         int n = s.size();
  5.         for (int i = 1; i <= n / 2; i++) {
  6.             if (n % i == 0) {
  7.                 bool res = true// 假设它是!!
  8.                 for (int j = i; j < n; ++j) {
  9.                     if (s[j] != s[j - i]) {
  10.                         res = false// 假设错误,下一个
  11.                         break;       // 此串不是答案,试试下一个
  12.                     }
  13.                 }
  14.                 if (res) //假设通过了检验return吧!
  15.                     return true;
  16.             }
  17.         }
  18.         return false;
  19.     }
  20. };
  1. 移动匹配

学会了string+string 的操作,竟然能实现字符串拼接。

学会了:std::string::npos 就是一个表示“没有找到”的特殊值

思路很简单,自己写一遍:

  1. class Solution {
  2. public:
  3.     bool repeatedSubstringPattern(string s) {
  4.         string t=s+s;
  5.         t.erase(t.begin());
  6.         t.erase(t.end()-1);
  7.         if(t.find(s)!=std::string::npos) return true;
  8.         return false;
  9.     }
  10. };
  1. KMP

还能用KMP?看看怎么个事

KMP:在一个串中查找是否出现过另一个子串

下标从1开始(图片来自王道-数据结构考研课程-BILIBILI):

唉算了,别折磨自己,精通两个方法足够了。

马上发布DAY10!

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值