ACWing算法基础课刷题记录2024-06-01--2day

831. KMP字符串

给定一个字符串 S𝑆,以及一个模式串 P𝑃,所有字符串中只包含大小写英文字母以及阿拉伯数字。

模式串 P𝑃 在字符串 S𝑆 中多次作为子串出现。

求出模式串 P𝑃 在字符串 S𝑆 中所有出现的位置的起始下标。

输入格式

第一行输入整数 N𝑁,表示字符串 P𝑃 的长度。

第二行输入字符串 P𝑃。

第三行输入整数 M𝑀,表示字符串 S𝑆 的长度。

第四行输入字符串 S𝑆。

输出格式

共一行,输出所有出现位置的起始下标(下标从 00 开始计数),整数之间用空格隔开。

数据范围

1≤N≤1051≤𝑁≤105
1≤M≤1061≤𝑀≤106

输入样例:
3
aba
5
ababa
输出样例:
0 2

 解题思路
暴力算法,每次移动一个字符,时间限制,比较费力

KMP算法

s1:计算前缀

s2:    找最长前后缀数组next

s3: 匹配

#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 1000010;
char p[N], s[M];
int n, m,  next1[N], f[N];
int main() {
//数组都是从下标1开始的
    cin >> n >> p + 1 >> m >> s + 1;

//求next的过程
for(int i=2,j=0;i<=n;i++){
    while(j!=0&&p[i]!=p[j+1])j=next1[j];
/*
寻找一个最长的后缀,它同时也是某个前缀。当p[i]不等于p[j+1]时,说明当前匹配的前缀-后缀不满足条件,需要缩短前缀长度,将j更新为nxt[j],即当前前缀的失效函数值。
*/
    if(p[i]==p[j+1]) j++;//当前字符与前缀的下一个字符匹配,则增加j的值,即增加前缀的长度
    next1[i]=j;/*将计算出的j值赋给nxt[i],表示当p[i]处的字符不匹配时,模式应该向右滑动到j的位置*/
}

    //kmp匹配过程
    for (int i = 1, j = 0; i <= m; i++) {
        while (j && s[i] != p[j + 1]) j = next1[j];
/*这个循环用于处理不匹配的情况,当j不为0且当前文本字符s[i]与模式中的下一个字符p[j + 1]不匹配时,将j更新为next1[j],即模式向右滑动的位数
*/
        if (s[i] == p[j + 1]) j++;
/*
如果当前文本字符与模式中的下一个字符匹配,则增加j的值,表示模式的前缀长度增加。
*/
        if (j == n) {//j=p的长度n,模式p完全匹配了文本s中的一部分,即从s[i-n+1]开始的子串
            //匹配成功了
            cout<< i - n<<" ";
            j =  next1[j];//这一步和匹配的第一步相同,找最长前后缀
        }
    }
    return 0;
}

看到一篇很好的文章

从头到尾彻底理解KMP(2014年8月22日版)_kmp算法 csdn-CSDN博客

835. Trie字符串统计

维护一个字符串集合,支持两种操作:

  1. I x 向集合中插入一个字符串 x𝑥;
  2. Q x 询问一个字符串在集合中出现了多少次。

共有 N𝑁 个操作,所有输入的字符串总长度不超过 105105,字符串仅包含小写英文字母。

输入格式

第一行包含整数 N𝑁,表示操作数。

接下来 N𝑁 行,每行包含一个操作指令,指令为 I x 或 Q x 中的一种。

输出格式

对于每个询问指令 Q x,都要输出一个整数作为结果,表示 x𝑥 在集合中出现的次数。

每个结果占一行。

数据范围

1≤N≤2∗1041≤𝑁≤2∗104

输入样例:
5
I abc
Q abc
Q ab
I ab
Q ab
输出样例:
1
0
1
/*最开始思路:一个字符串数组,vector<string>,I操作就是在字符串后面新插入字符串
Q操作,查找这个字符串在不在数组str,用find函数,在查找时,发现比较费时间,不只是查找是否存在,
还需要返回次数。所以我们使用一个unordered_map来维护字符串及其出现的次数。每次插入操作I x时,
我们就在unordered_map中增加字符串x的计数。每次查询操作Q x时,我们输出字符串x的当前计数
*/
//unordered_map的底层实现是哈希表,因此插入和查询操作的平均时间复杂度都是O(1),这足以满足数据范围的要求
//实现字符串计数的一种高效方式,可以快速处理大量的插入和查询操作
#include<bits/stdc++.h>
using namespace std;
 unordered_map<string, int> counts;
int m;
int main(){
    cin>>m;
    char a;
    string s;
    for (int i = 0; i < m; ++i) {
        cin >> a >> s;
        if (a == 'I') {
            counts[s]++;
            /*
            如果s已经存在于unordered_map中,则将其对应的值加1。
            如果s不在unordered_map中,则将其添加到unordered_map中,并将其对应的值设置为1。*/
        } else if (a == 'Q') {
            cout << counts[s] << endl;
        }
    }
    return 0;
}

 不过这个题是一个典型的tries树的题,模板题

 

143. 最大异或对

在给定的 N𝑁 个整数 A1,A2……AN𝐴1,𝐴2……𝐴𝑁 中选出两个进行 xor𝑥𝑜𝑟(异或)运算,得到的结果最大是多少?

输入格式

第一行输入一个整数 N𝑁。

第二行输入 N𝑁 个整数 A1𝐴1~AN𝐴𝑁。

输出格式

输出一个整数表示答案。

数据范围

1≤N≤1051≤𝑁≤105,
0≤Ai<2310≤𝐴𝑖<231

输入样例:
3
1 2 3
输出样例:
3

//Trie树快速存储字符集合和快速查询字符集合
#include <iostream>
using namespace std;
const int N = 100010;
//son[][]存储子节点的位置,分支最多26条;
//cnt[]存储以某节点结尾的字符串个数(同时也起标记作用)
//idx表示当前要插入的节点是第几个,每创建一个节点值+1
int son[N][26], cnt[N], idx;
char str[N];

void insert(char *str)
{
    int p = 0;  //类似指针,指向当前节点
    for(int i = 0; str[i]; i++)
    {
        int u = str[i] - 'a'; //将字母转化为数字,树的根节点是0,其他的字母,减去小写字母之后,最多有25个,0-25,就是第一层树,可以包括a-z
        if(!son[p][u]) son[p][u] = ++idx;   //该节点不存在,创建节点
        p = son[p][u];  //使“p指针”指向下一个节点,如果说这个节点是存在的,那就只指向这个节点
    }
    cnt[p]++;  //结束时的标记,也是记录以此节点结束的字符串个数
}

int Que(char *str)
{
    int p = 0;
    for(int i = 0; str[i]; i++)
    {
        int u = str[i] - 'a';
        if(!son[p][u]) return 0;  //该节点不存在,即该字符串不存在
        p = son[p][u]; 
    }
    return cnt[p];  //返回字符串出现的次数
}

int main()
{
    int m;
    cin >> m;

    while(m--)
    {
        char a;
        cin>>a>>str;

        if(a == 'I') insert(str);
         else cout<< Que(str)<<endl;
    }

    return 0;
}


 两种方式运行时间相差不大

//这个题一开始是没有思路的,一点思路都没有,参考了评论区解题思路
#include<bits/stdc++.h>
using namespace std;
int const N=100010,M=31*N;
int n;
int a[N];
int son[M][2],idx;
//M最大可以创建多少条路
void insert(int x)//这一步和上一个插入是一样的操作
{
    int p=0;  //根节点
    for(int i=30;i>=0;i--)
    {
        int u=x>>i&1;   /取X的第i位的二进制数是什么  x>>k&1(前面的模板)
        if(!son[p][u]) son[p][u]=++idx; ///如果插入中发现没有该子节点,开出这条路
        p=son[p][u]; //指针指向下一层
    }
}
int find(int x)//查找xor
/*
 // 对于当前位的二进制数
        // 尽可能往其出现过的相反的方向走,假设当前位 u = 0
        // 若1的那个方向的 trie树枝干被创建则向那个方向走
        // 若没有被创建,则先将就一下走0的方向
*/
{
    int p=0;int res=0;
    for(int i=30;i>=0;i--)
    {                               ///从最大位开始找
        int u=x>>i&1;
        if(son[p][!u]) 如果当前层有对应的不相同的数
        {   ///p指针就指到不同数的地址

          p=son[p][!u];
          res=res*2+1;
        }                                              
        else    
        {
            p=son[p][u];
            res=res*2+0;
        }
    }
    return res;
}
int main(void)
{
    cin.tie(0);//初始化根节点
    cin>>n;
    idx=0;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        insert(a[i]);
    }
    int res=0;
    for(int i=0;i<n;i++)
    {   
        res=max(res,find(a[i])); 
    }
    cout<<res<<endl;
}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: acwing算法基础课是一门针对算法学习的在线课程,在这门课程中,学生可以系统地学习和掌握算法基础知识,提高编程水平。为了方便学生学习,acwing提供了网盘服务。 acwing算法基础课网盘是一个用于存储课程资源的平台。通过这个网盘,学生可以下载课程讲义、代码模板以及补充材料等。这些资源都经过精心整理,供学生们参考和学习。 网盘中的资源是按照课程章节进行分类的,学生可以根据自己的学习需要,选择性地下载所需的资料。同时,网盘还提供了搜索功能,方便学生快速定位和获取所需资料。 acwing算法基础课网盘的使用对于学生们的学习非常有帮助。通过下载和学习这些资源,学生们可以更好地理解课程内容,加深对算法的理解。此外,学生们还可以通过研究代码模板,学习优秀的编程思想和技巧,提高自己的编程能力。 总之,acwing算法基础课网盘是一项非常便利和实用的服务,为学生们提供了更加全面和深入的学习资源,帮助他们更好地掌握和运用算法知识。 ### 回答2: acwing算法基础课是一门优质的算法学习资源,其中的课程内容丰富多样,涵盖了算法基础知识、数据结构、动态规划、图论等等。很多学习者都认为这门课程对他们的算法学习有很大的帮助。 网盘是指以网络为媒介,提供文件存储和下载服务的云存储平台。acwing算法基础课也提供了网盘服务,方便学习者下载课程资料并进行学习。 通过acwing算法基础课网盘,学习者可以方便地获取到课程的各种学习资料,包括讲义、习集、代码示例等。这些资料可以帮助学习者更好地理解和掌握课程的内容。此外,网盘还提供了上传和分享功能,学习者可以将自己的学习心得、代码等资料分享给其他学习者,促进学习者之间的互相学习和交流。 acwing算法基础课网盘的优点不仅仅是方便快捷的下载和分享功能,还包括安全可靠的存储环境。学习者可以放心地将自己的学习资料上传到网盘进行备份,减少数据丢失的风险。同时,网盘还提供了多种存储空间容量的选择,满足学习者不同的需求。 总的来说,acwing算法基础课网盘为学习者提供了方便、安全和多样化的学习资源下载和分享服务,为学习者的算法学习和进步提供了有力的支持。如果你对算法感兴趣,我推荐你去尝试一下这门精彩的课程!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值