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.九峰与蛇形填数

题目描述

蛇形填数是一道经典的入门题,但是九峰有自己的想法,他认为盘着的蛇不是一条好蛇,只有不断前进才能突破自 我,变成真龙,因此,相比于将矩阵填数成回型(盘踞的蛇):

\[\begin{array}{lll} 1 & 2 & 3 \\ 8 & 9 & 4 \\ 7 & 6 & 5 \end{array} \]

九峰更憙欢将矩阵填成"S"型(行走的蛇):

\[\begin{array}{lll} 1 & 2 & 3 \\ 6 & 5 & 4 \\ 7 & 8 & 9 \end{array} \]

现在给你一个 \(n^{*} n\) 的初始全零的矩阵,请你将其按第二种方法填数,但是这样子太过简单,所以每一次操作九峰会选 择一个子矩阵,请你在其子矩阵上进行填数,并在最后输出整个矩阵

输入描述:

\[\text { 第一行两个整数 } \mathrm{n}, \mathrm{m} \text {, 表示矩阵的大小和操作次数 }(n \leq 2000, m \leq 3000) \]

接下来 \(\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;
}
posted @ 2022-03-24 18:11  zyy2001  阅读(24)  评论(0编辑  收藏  举报