2021牛客寒假算法基础集训营4
题目链接
2021牛客寒假算法基础集训营4
B.武辰延的字符串
题目描述
众所周知,武辰延很喜欢字符串。
这天,他对着两个字符串 \(s\) 和 \(t\) 发呆,他发现这两个串的前缀有很多相似的地方, \(s\) 的两个前缀连接起来竟也是 \(t\) 的 前缀。
武辰延想知道有多少对 \(s\) 的非空前缀连接起来是 \(t\) 的前缀。
形式化地讲,我们把 \(s_{i}\) 看作字符串 \(\mathrm{s}\) 长度为 \(\mathrm{i}\) 的前缀。
对于一对前缀 \(\left(s_{i}, s_{j}\right.\) ) (允许 \(\mathrm{i}=\mathrm{j}\) )而言,当满足 \(s_{i}+s_{j}=t_{i+j}\) 时,我们认为这两个 \(\mathrm{s}\) 的前缀㔙接后等于 \(\mathrm{t}\) 的一个前 叕。
两对 \(\mathrm{s}\) 的前缀 \(\left(s_{i}, s_{j}\right)\) 与 \(\left(s_{i^{\prime}}, s_{j^{\prime}}\right)\) 不同当且仅当 \(i \neq i^{\prime}\) 或 \(j \neq j^{\prime}\) 。
输入描述:
第一行一个字符串 \(\mathrm{s}\) 。
第二行一个字符串 \(t\) 。
其中 \(1 \leq|s|,|t| \leq 1 e 5\), 只包含小写字母。
输出描述:
输出一行一个整数, 表示满足条件的前缀的对数。
解题思路
二分,哈希
对于 \(s\) 的每一个固定前缀的 \(s_i\),要求 \(t\) 的前缀 \(t_i=s_i\),\(i\) 处的贡献为 \(t\) 的 \(i\) 后面的子串和 \(s\) 前缀的最长公共长度,可以预处理两个字符串的哈希值,然后二分长度
- 时间复杂度:\(O(nlogn)\)
代码
// Problem: 武辰延的字符串
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/30907/B
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
// #define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
using ull=unsigned long long;
const int N=1e5+5;
string s,t;
ull res;
ull hs[N],ht[N],h[N],p=131;
ull get(int l,int r,ull ha[])
{
return ha[r]-ha[l-1]*h[r-l+1];
}
bool Hash(int x,int len)
{
return get(1,len,hs)==get(x+1,x+len,ht);
}
int main()
{
cin>>s>>t;
int n=s.size(),m=t.size();
s=' '+s;
t=' '+t;
h[0]=1;
for(int i=1;i<=max(n,m);i++)
{
h[i]=p*h[i-1];
if(i<=n)
hs[i]=hs[i-1]*p+s[i]-'a'+1;
if(i<=m)
ht[i]=ht[i-1]*p+t[i]-'a'+1;
}
for(int i=1,j=1;i<=n&&j<=m&&s[i]==t[j];i++,j++)
{
int l=0,r=m-(j+1)+1;
while(l<r)
{
int mid=l+r+1>>1;
if(Hash(j,mid))l=mid;
else
r=mid-1;
}
res+=r;
}
cout<<res;
return 0;
}
G.九峰与蛇形填数
题目描述
蛇形填数是一道经典的入门题,但是九峰有自己的想法,他认为盘着的蛇不是一条好蛇,只有不断前进才能突破自 我,变成真龙,因此,相比于将矩阵填数成回型(盘踞的蛇):
九峰更憙欢将矩阵填成"S"型(行走的蛇):
现在给你一个 \(n^{*} n\) 的初始全零的矩阵,请你将其按第二种方法填数,但是这样子太过简单,所以每一次操作九峰会选 择一个子矩阵,请你在其子矩阵上进行填数,并在最后输出整个矩阵
输入描述:
接下来 \(\mathrm{m}\) 行, 每行输入三个正整数 \(\mathrm{x}, \mathrm{y}, \mathrm{k}\), 表示在以 \((\mathrm{x}, \mathrm{y})\) 为左上角, 边长为 \(\mathrm{k}\) 的方阵内填数 \((1 \leq x, y \leq\) \(n, \max (x+k-1, y+k-1) \leq n)\)
输出描述:
输出一个\(n*n\)的矩阵,表示最后的结果
示例1
输入
3 2
1 1 3
2 2 2
输出
1 2 3
6 1 2
7 4 3
解题思路
思维
显然,从后往前填数更优,暴力填数时很多位置可能会重复枚举,可以考虑优化:设置 \(ne[i][j]\),表示位置位于 \((i,j)\) 的数右边连续的最后一个位置的纵坐标,如果当前数已填,则走到该数右边第一个未填的位置,这样每次填数不会重复走到一个位置
- 时间复杂度:\(O(n^2)\)
代码
// Problem: 九峰与蛇形填数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/30907/G
// Memory Limit: 524288 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=2005;
int n,m,res[N][N],ne[N][N];
pair<PII,int> a[3005];
int main()
{
cin>>n>>m;
for(int i=m;i;i--)cin>>a[i].fi.fi>>a[i].fi.se>>a[i].se;
for(int i=1;i<=m;i++)
{
int x=a[i].fi.fi,y=a[i].fi.se,len=a[i].se;
for(int j=x;j<=x+len-1;j++)
for(int k=y;k<=y+len-1;k++)
if(res[j][k])k=ne[j][k];
else
{
ne[j][k]=y+len-1;
if((j-x)&1)res[j][k]=len*(j-x)+len-k+y;
else
res[j][k]=len*(j-x)+k-y+1;
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)cout<<res[i][j]<<(j<n?' ':'\n');
return 0;
}