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
,看到这个数据范围有点退缩了,果然肉眼可见的综合素质下降。有三种方法,记忆化搜索将已经更新过的记录下来进行剪枝;或者人人为我型通过结构体将二维变成一维,按高度排序然后通过上下左右更新当前位置;第三种和第二种类似,我为人人,只不过通过当前点更新四周的点。
第三题:路径最大异或和
题意
给你一个二叉树,输入需要处理一下。可以从任意节点往下走,求经过的路径节点权值异或最大和。
数据范围
节点数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
*/