若干简单题

不重复的子段数量

问题 给你一个由小写字符组成字符串,问由多少连续子串没有重复字符。
解法一
枚举右端点,因为只由26个小写字母组成,所以可能合法的左端点只有26个,依次判断即可。
复杂度O(26n)。
解法二
f ( r ) f(r) f(r)表示r对应的最小且合法的左端点。
我们发现有性质 f ( r ) < = f ( r + 1 ) f(r) <= f(r + 1) f(r)<=f(r+1)
因此,左右端点各扫描一次,复杂度O(n)。
核心代码

int solve() {
	int ans = 0;
	for (int l = 1, r = 1; r <= n; r++) {
		cnt[str[r]]++;
		while (cnt[str[r]] > 1) cnt[str[l++]]--;
		ans += r - l + 1;
	}
	return ans;
}

方格染色

问题
给定n个格子排成一条直线,对每个格子染黑色或者白色,要求黑色的格子不能相邻,问染色方案数。
解法一
考虑使用动态规划,设dp[i][0]表示已经合法的染了前i个格子,且第i个格子的颜色是白色,dp[i][1]表示已经合法的染了前i个格子,且第i个格子的颜色是黑色。
当第i+1个格子要染白色时,第i个格子染黑白都可以。dp[i + 1][0]=dp[i][0]+dp[i][1]。
当第i+1个格子要染黑色时,第i个格子只能染白色。dp[i+1][1]=dp[i][0]。
复杂度O(n)。
解法二
设f(n)表示染n个格子,且每个格子都没有额外限制的方案数。
若第1个格子染白色,则后面n-1个格子没有额外限制,有f(n-1)种方案。
若第1个格子染黑色,则第二个格子一定染白色,后面n-2个格子没有额外限制,有f(n-2)种方案。
所以f(n)=f(n-1)+f(n-2)。
复杂度O(n)
解法三
考虑构造如下矩阵。
[ d p [ i + 1 ] [ 0 ] d p [ i + 1 ] [ 1 ] ] = [ 1 1 1 0 ] [ d p [ i ] [ 0 ] d p [ i ] [ 1 ] ] \begin{bmatrix} dp[i+1][0] \\ dp[i+1][1] \end{bmatrix}=\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}\begin{bmatrix} dp[i][0] \\ dp[i][1] \end{bmatrix} [dp[i+1][0]dp[i+1][1]]=[1110][dp[i][0]dp[i][1]]

[ f ( i ) f ( i − 1 ) ] = [ 1 1 1 0 ] [ f ( i − 1 ) f ( i − 2 ) ] \begin{bmatrix} f(i) \\ f(i-1) \end{bmatrix}=\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}\begin{bmatrix} f(i-1) \\ f(i-2) \end{bmatrix} [f(i)f(i1)]=[1110][f(i1)f(i2)]
问题就转化成了系数矩阵的n次幂。快速幂处理。
复杂度 O ( l o g 2 n ) O(log_2n) O(log2n)

共线

问题
给定n个二维平面上的点,问是否存在一条直线至少穿过n/2个点。
解法一
枚举两个点,算斜率和截距,最后判断是否有这条直线。
复杂度 O ( n 2 ) O(n^2) O(n2)
解法二
假设存在这样一条合法的直线,那么我们随机选一个点,且在这条直线上的概率是1/2。那么我们随机选择两个点,在这条直线上的概率是1/4,不在的概率是3/4。每次验证在这条直线上的点的个数,如果合题意,显然存在。如果不是那条直线,那么误判的概率是3/4。假设我们做T次这样的判定,都不存在合法的直线,我们断言不存在这样的直线的误判概率是 ( 3 / 4 ) T (3/4)^T (3/4)T
当T取30时,误判的概率已经小于万分之一。当然,选择合适的T即可。
复杂度 O ( T n ) O(Tn) O(Tn)

k-element

问题
给你无序的n个数,求第k小元素。这是一个经典问题,已经被c++收录为nth_element函数。
解法一
将原数组排序,直接可得第k小元素。
复杂度取决于排序算法。
解法二
我们随机的从序列中选一个数x,把其他的数分为两类,有a个小于x的,有b个大于x的。
如果a+1==k,则x就是答案。
如果a>=k,则对小于x部分元素求第k小元素。
如果a<k,则对大于x部分求第k-a-1小元素。
因为x是随机选的,每次期望舍弃序列的一半,复杂度=O(n+n/2+n/4+…)=O(n)

3和5和好数

定义 一个数能被3整除或者被5整除则称为好数。比如3,5,6,9,10,12,15都是好数。
问题一: [ 1 , n ] [1,n] [1,n]中有多少好数。
f ( n ) = n / 3 + n / 5 − n / 15 f(n) = n/3+n/5-n/15 f(n)=n/3+n/5n/15
容斥原理,三的倍数有n/3个,5的倍数有n/5个,但是十五的倍数被算了两次。
复杂度 O ( 1 ) O(1) O(1)
问题二: 问第n个好数是多少。
解法一:
直接枚举,如果是好数就计数。我们发现答案不超过5n。
复杂度 O ( n ) O(n) O(n)
解法二:
我们能够发现f(n)是个单调不降函数,因此可以二分。
假设第n个好数在区间[l,r]里,答案肯定在[1,5n]这个区间里。取mid=(l+r)/2,如果f(mid)<n,则答案在[mid+1,r]里,否则答案在[l,mid]里。
每次区间缩小一半,经过 l o g 2 n log_2n log2n次区间大小缩小为1,即为答案。
复杂度 O ( l o g 2 n ) O(log_2n) O(log2n)

过河

问题 有n个人需要过河,第i个人的体重是wi,保证体重按照递增的顺序给出。一艘船最多能坐两个人,最大承重W,问最小需要几艘船。
解法:
考虑贪心的解决这个问题,两个人的体重和尽可能的接近W。
复杂度O(n)
核心代码

int ans = 0;
for (int l = 1, r = n; l <= r;) {
	if (w[l] + w[r] > W) {
		ans++; r--;
	}else {
		ans++; l++; r--;
	}
}

k次方和

问题 ∑ i = 1 n i k % 2021325 \sum_{i=1}^ni^k\%2021325 i=1nik%2021325,。
解法一
直接暴力计算,复杂度O(nk)。
解法二
考虑如何快速计算 i k i^k ik,我们分别计算 i 1 , i 2 , i 4 , i 8 , i 16 . . . i^1,i^2,i^4,i^8,i^{16}... i1,i2,i4,i8,i16...后一项是前一项的平方,而k一定能写成不超过logk个数的加和,如13=1+4+8, i 13 = i ∗ i 4 ∗ i 8 i^{13}=i*i^4*i^8 i13=ii4i8
复杂度 O ( n l o g 2 k ) O(nlog_2k) O(nlog2k)
核心代码

int qpow(int x, int n, int mod) {
	int res = 1;
	while (n > 0) {
		if (n % 2) res = 1LL * res * x % mod;
		x = 1LL * x * x % mod;
		n /= 2;
	}
	return res;
}

解法三
我们发现合数能够拆成两个更小的已经被计算过的数,只要求质数的k次幂即可。质数的密度是 n / l o g 2 n n/log_2n n/log2n的。
配合线性筛,复杂度O(n)。

int solve() {
	int ans = 1;
	memset(is_pri, true, sizeof(is_pri));
	for (int i = 2; i < N; i++) {
		if (is_pri[i]) {
			f[i] = qpow(i, k);
			pri[tot++] = i;
		}
		for (int j = 0; 1LL * i * pri[j] < N; j++) {
			f[i * pri[j]] = 1LL * f[i] * f[pri[j]] % MOD;
			is_pri[i * pri[j]] = false;
			if (i % pri[j] == 0) break;
		}
		ans = (ans + f[i]) % MOD;
	}
	return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值