2020.9.9华为笔试记忆:KMP+记忆化搜索+字典树

2020.9.9华为笔试

当然,出现在我博客中的笔试都不是我自己的笔试(人家也不给我发笔试链接,小声bibi,诶,好像我也没投,hhhahahha


记者:为什么要做笔试?

我:生活无聊了喏,肯定要做啊,不做又没有乐趣

记者:你有手有脚的怎么不去进厂?

我:进厂这方面…进厂是不可能进厂的, 这辈子不可能进厂的,投简历又不给过,就是蹭笔试这种东西,才能维持得了生活这样子

记者:那你觉得刷题好还是996好?

我:996的感觉像回一样,我一年回一次家,大年三十晚上我都不回去,就平时家里有点事,我就回去看看这样子,996的感觉比家里面好多~了,在家里面一个人很无聊,又没有朋友玩,没有女朋友玩 ,进了里面个个都是人才,说话又好听,超喜欢在里面

第一题:完美排列
题意

简单来说,给你两个模式串Pa,Pb,再给你两个主串Sa,Sb。找出主串中第一个位置使得两个模式串在发分别两个主串同时匹配上,若无法匹配输出0

数据范围

模式串长度1e5,主串长度1e6,数字范围[0, 5]

解题思路

很显然是KMP算法,但是两个串要对应同时匹配。再观察到数据范围,可以将两个串合并成一个串,类似hash的方式对应相加即可,比如:Pa * 5 + Pb ,模式串和主串计算方式要一致。这样就可以进行独一无二的映射。然后做一次KMP即可,找出位置和匹配计数是一样的。

奈何多年没做题,没想到hash映射的的方式,而选择了作差的方法,这样通过率只有90%

const int N=2e6+10;
int n,m;
int str[N] ;		//主串
int p[N];		//模式串
int a[N],b[N];
int nextval[N];
void get_nextval()//获取nextval数组
{
    int i = -1;
    int j = 0;
    nextval[0] = -1;
    while(j < m)
    {
        if(i == -1 || p[i] == p[j])
        {
            i++;
            j++;
            if(p[i] != p[j])
            {
                nextval[j] = i;
            }
            else
                nextval[j] = nextval[i];
        }
        else
            i = nextval[i];
    }
}
void KmpSearch() //kmp匹配
{
    queue<int>q;
    while(!q.empty()) q.pop();
    int i = 0;
    int j = 0;
    int result = 0;		//一共有多少个匹配
    while(i < n)
    {
        if((str[i] == p[j]&&b[i]==a[j]) || j == -1)
        {
            i++;
            j++;
        }
        else
            j = nextval[j];
        if(j == m)			//表示完成一个匹配
        {
//            result++;
            printf("%d\n",i-j+1); //输出这个匹配在主串开始的位置(注意,主串是从0开始的)
            j = nextval[j];
            return ;
        }
    }
    puts("0");
//    printf("\n");
//    return result;
}
int main()
{
    while(~scanf("%d",&m))
    {
        int tmp;
        for(int i=0;i<m;i++) // 模式串
        {
            scanf("%d",&p[i]);
            a[i]=p[i];
        }
        for(int i=0;i<m;i++){
            scanf("%d",&tmp);
            p[i]-=tmp;
        }
        scanf("%d",&n);
        for(int i=0;i<n;i++) // 模式串
        {
            scanf("%d",&str[i]);
            b[i]=str[i];
        }
        for(int i=0;i<n;i++){
            scanf("%d",&tmp);
            str[i]-=tmp;
        }
        get_nextval();
        KmpSearch();
    }
    return 0;
}


/*
3
1 2 3
3 2 1
6
1 2 3 3 2 1
3 2 1 1 2 3

3
1 2 3
3 2 1
6
1 2 1 2 3 3
5 4 3 2 1 1

*/
第二题:最长水沟
题意

给你一个1000*1000 的二维数字数组,上下左右视为邻接,求最长单调序列长度。

解题思路

这道题其实2016年做过,NYOJ和POJ的滑雪,不过当时做的数据范围是100*100,看到这个数据范围有点退缩了,果然肉眼可见的综合素质下降。有三种方法,记忆化搜索将已经更新过的记录下来进行剪枝;或者人人为我型通过结构体将二维变成一维,按高度排序然后通过上下左右更新当前位置;第三种和第二种类似,我为人人,只不过通过当前点更新四周的点。

详见POJ 1088滑雪(3种解法)

第三题:路径最大异或和
题意

给你一个二叉树,输入需要处理一下。可以从任意节点往下走,求经过的路径节点权值异或最大和。

数据范围

节点数1e5,节点权值 2^30 - 1。C++时限1s

解题思路

异或最值问题一般需要用字典树,这个题有点特殊,但是参考以前做过的题比如:hdu-4825 Xor Sum。这个题我们可以用dfs序把每个点到根节点的异或和存储为数组形式,然后用[HDU425]的解题方式即可:即查询当前数与前面的某个数的异或最大值,查询完毕将当前数插入字典树。
代码是自己写完的,但是没有提交,这个题数据比较弱,用暴力的方式通过了。

const int N=2e6+10;
int rt,a[N],v[N],s[N][2];
int b[N];
vector<int>g[N];
int in[N],out[N],top;
void init()
{
    top=0;
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(s,0,sizeof(s));
    for(int i=0; i<N; i++)
        g[i].clear();
}
void dfs(int u)
{
    in[u]=++top;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        a[v]^=a[u];
        dfs(v);
    }
    out[u]=++top;
    b[in[u]]=a[u];
    b[out[u]]=a[u];
}
void insert(int x,int id)
{
    int i,u,op;
    u=0;
    for(i=31; i>=0; i--)
    {
        op=((x&(1<<i))!=0);
        if(s[u][op]==0)
            s[u][op]=rt++;
        u=s[u][op];
    }
    v[u]=id;
}
int get(int x)
{
    int i,u,op;
    u=0;
    for(i=31; i>=0; i--)
    {
        op=((x&(1<<i))!=0);
        if(s[u][op^1]!=0)              //尽可能走与当前位不同的点
            u=s[u][op^1];
        else
            u=s[u][op];
    }
    return b[v[u]];
}
int main()                                      //将每个数拆分成二进制从高位到低位插入到trie树中,
{
    int n;
    while(~scanf("%d",&n))
    {
        init();
        int id,l,r;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&id);
            scanf("%d%d%d",&a[id],&l,&r);
            if(l!=-1)
            {
                in[l]++;
                g[id].push_back(l);
            }
            if(r!=-1)
            {
                in[r]++;
                g[id].push_back(r);
            }
        }
        for(int i=1; i<=n; i++)
            if(in[i]==0)
                dfs(i);
        int ans=0;
//        for(int i=1;i<=n;i++)
//            printf("%d  %d  %d\n",i,b[in[i]],b[out[i]]);
        for(int i=1;i<=top;i++)
        {
            ans=max(ans,b[i]^get(b[i]));    // get(x)查询前面的数中,与当前数x异或值最大的那个数
            insert(b[i],i);  // 插入当前数
        }
        cout<<ans<<endl;
    }
    return 0;
}
/*

5
1 1 2 3
2 4 -1 -1
3 2 -1 4
4 5 -1 5
5 3 -1 -1

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
strstr是C语言中的一个函数,用于在一个字符串中查找指定字符串的第一次出现的位置。给定一个字符串haystack和一个字符串needle,在haystack中找到needle第一次出现的位置,返回该位置的指针。 为了增强strstr函数的功能,我们可以通过以下方式进行改进: 1. 使用KMP算法:KMP算法是一种用于在字符串中查找子串的高效算法,它通过预处理子串的信息,可以在查找过程中跳过一些不必要的比较。在实现strstr函数时,我们可以使用KMP算法来提高查找的效率。 2. 多线程并行查找:对于较长的字符串,为了提高查找的速度,我们可以使用多线程并行方式进行查找。将字符串分成多个子串,每个子串由一个线程负责查找,然后合并各个线程的查找结果,得到最终的查找位置。 3. 支持正则表达式:考虑到有些情况下,用户可能需要使用更复杂的匹配规则进行查找,我们可以增加对正则表达式的支持。通过引入正则表达式引擎,我们可以让strstr函数能够支持更加灵活的字符串查找。 4. 错误处理和边界情况处理:在改进strstr函数的过程中,我们需要考虑各种边界情况和错误处理。例如,对于空字符串的输入,我们可以提前处理并返回特定的结果,以避免发生异常情况。 总之,通过使用KMP算法、多线程并行查找、支持正则表达式以及做好错误处理和边界情况处理,我们可以在提升strstr函数的查找效率的同时,增强其功能和适用性。这样的改进可以使华为OD机试题中的strstr函数更加强大和实用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值