阿里笔试4.19 题解

T1 给定一个序列,问有多少个区间的众数次数>=k

sol:考虑two-pointer。枚举左端点,寻找最小的右端点是的众数次数>=k

推导后发现需要支持以下功能

1.增加某一个数的出现次数

2.减少某一个数的出现次数

3.查询众数的出现次数(即出现次数最多的数字的出现次数)

这些功能可以抽象化可以理解为维护一个集合,支持单点修改,支持查询最大值

因此考虑使用堆来实现,具体实现过程类似dijkstra找最短节点的过程。

代码如下:

#include<bits/stdc++.h>
#define N 440000
#define ll long long 
using namespace std;
struct node
{
	int x,k;
};
bool operator<(node a,node b)
{
	return a.k<b.k;
}
priority_queue<node>q;
int a[N],w[N],cnt[N];
int query()
{
	while(q.top().k!=cnt[q.top().x])q.pop();
	return q.top().k;
}
void update(int x,int k)
{
	cnt[x]=k;
	q.push({x,k});
}
int main()
{
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i],w[i]=a[i];
	sort(w+1,w+n+1);
	int m=unique(w+1,w+n+1)-w-1;
	for(int i=1;i<=n;i++)a[i]=lower_bound(w+1,w+m+1,a[i])-w;
	for(int i=1;i<=m;i++)q.push({i,0});
	ll ans=0;
	for(int i=1,j=0;i<=n;i++)
	{
		while(j<n&&query()<k)
		{
			j++;
			update(a[j],cnt[a[j]]+1);
		}
		if(query()>=k)ans+=n-j+1;
		update(a[i],cnt[a[i]]-1);
	}
	cout<<ans<<endl;
	return 0;
}

T2 题意大概就是模拟一个消消乐的过程

sol:bfs暴力模拟消除的过程,而下落的过程可以逐行维护每一列的高度来模拟实现。

代码如下:

#include<bits/stdc++.h>
#define N 110
using namespace std;
struct point
{
	int c,w;
};
point a[N][N];
struct node
{
	int x,y;
};
queue<node>q;
int n,m,qnum,h[N];
int work(int sx,int sy)
{
	if(a[sx][sy].c==0)return 0;
	q.push({sx,sy});
	int c=a[sx][sy].c,ans=0;
	while(!q.empty())
	{
		node o=q.front();
		q.pop();
		
		int x=o.x,y=o.y;
		
		ans+=a[x][y].w;
		a[x][y]={0,0};
		
		if(a[x+1][y].c==c)q.push({x+1,y});
		if(a[x-1][y].c==c)q.push({x-1,y});
		if(a[x][y+1].c==c)q.push({x,y+1});
		if(a[x][y-1].c==c)q.push({x,y-1});
	}
	for(int i=1;i<=m;i++)h[i]=n+1;
	for(int i=n;i>=1;i--)
	{
		for(int j=1;j<=m;j++)
		if(a[i][j].c)
		{
			point t=a[i][j];
			a[i][j]={0,0};
			a[--h[j]][j]=t;
		}
	}
	
	return ans;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		string s;
		cin>>s;
		a[i][j].c=s[0]-'a'+1;
		a[i][j].w=s[1]-'0';
	}
	cin>>qnum;
	for(int o=1;o<=qnum;o++)
	{
		int x,y;
		cin>>x>>y;
		printf("%d\n",work(x,y));
	}
	return 0; 
}

T3 求所有长度为n的字符串有多少个长度为3的回文子串

考虑动态规划

考虑一个字符串的最后两个字符的形式,要么是"aa",要么是“ab”。

不妨记

"aa"结尾的字符串的数量为x[n],记这些字符串的权值和为fx[n]。

记"ab"结尾的字符串的数量为y[n],记这些字符串的权值和为fy[n]。

"aa"有两种转移方式——>"aaa",“aab”

其中"aaa"有1种方式。

其中"aab"有25种方式,指的是填与"a"不同的25个字符。

"ab"有三种转移方式——>"aba",“abb”,"abc"

其中"aba"有1种方式。

其中"abb"有1种方式。

其中"abc"有24种方式,指的是填与"a"不同的25个字符。

这里简单写一下dp的转移方程

x[n]=x[n-1]+y[n-1]

fx[n]=x[n-1]+fx[n-1]+y[n-1]

y[n]=25x[n-1]+25y[n-1]

fy[n]=25fx[n-1]+y[n-1]+25fy[n-1]

直接暴力dp复杂度为O(n)

考虑使用矩阵快速幂优化(类似快速求斐波那契数列)

即可做到O(logn)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mo=1e9+7;
struct matrix
{
	ll s[5][5];
	matrix()
	{
		memset(s,0,sizeof(s));
	}
};
matrix I()
{
	matrix ans;
	for(ll i=0;i<5;i++)ans.s[i][i]=1;
	return ans; 
}
matrix operator*(matrix a,matrix b)
{
	matrix ans;
	for(ll i=0;i<5;i++)
	for(ll j=0;j<5;j++)
	for(ll k=0;k<5;k++)
	ans.s[i][j]=(ans.s[i][j]+1ll*a.s[i][k]*b.s[k][j]%mo)%mo;
	return ans;
}
matrix ksm(matrix x,ll k)
{
	matrix ans=I();
	while(k)
	{
		if(k&1)ans=ans*x;
		k>>=1;
		x=x*x;
	}
	return ans;
}
matrix f,g;
int main()
{
	ll n;
	cin>>n;
	if(n<=2)
	{
		cout<<0;
		return 0;
	}
	
	f.s[0][0]=26;
	f.s[0][1]=0;
	f.s[0][2]=26*25;
	f.s[0][3]=0;
	
	g.s[0][0]=1;
	g.s[2][0]=1;
	
	g.s[0][1]=1;
	g.s[1][1]=1;
	g.s[3][1]=1;
	
	g.s[0][2]=25;
	g.s[2][2]=25;
	
	g.s[0][3]=0;
	g.s[1][3]=25;
	g.s[2][3]=1;
	g.s[3][3]=25;
	
	f=f*ksm(g,n-2);
	
	ll ans=(f.s[0][1]+f.s[0][3])%mo;
	cout<<ans;
	return 0;
}
posted @ 2023-04-19 21:43  Creed-qwq  阅读(31)  评论(0编辑  收藏  举报