阿里笔试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;
}