T^T_题解

【题解提供者】吴立强

解法一(思路来源:刘开泰的代码)

思路

当直接算没有太好的方式,那么可以尝试枚举不一样的东西。

若枚举第一个 T 的位置( i ∈ [ 1 , n ] i\in[1, n] i[1,n]),那么计算答案时还需要枚举中间 ^ 的位置( j ∈ [ i + 1 , n ] j\in[i+1, n] j[i+1,n]),然后再枚举最后一个 T 的位置( k ∈ [ j + 1 , n ] k\in[j+1,n] k[j+1,n])这样直接枚举需要计算的次数可以达到 O ( n 3 ) O(n^3) O(n3) 级别,显然不够。

一个容易想到的优化是最后一个 T 可以通过预处理,计算出区间 [ j + 1 , n ] [j+1,n] [j+1,n] 中有多少个 T,每个 T 对答案都可以造成 +1 的影响,这个预处理可以通过后缀和完成。那么原算法需要的计算次数可以优化成 O ( n 2 ) O(n^2) O(n2) 级别,仍然不够。

此时可以考虑新的思维枚举中间字符 ^ 的位置( i ∈ [ 1 , n ] i\in[1,n] i[1,n])又会怎么样?

通过组合数学中的乘法原理可以得出对于给定位值( i i i)的字符 ^,字符串中包含这个字符的 T^T 子字符串数量为:(区间 [ 1 , i − 1 ] [1,i-1] [1,i1] 中所包含 T 的数量) × \times × (区间 [ i + 1 , n ] [i+1,n] [i+1,n] 中所包含 T 的数量)。而上面的两个部分都可以通过前\后缀和的预处理来完成。

代码

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1000009;
char s[N];
int l[N], r[N];

int main() {
	/// 读入一个字符串,但是从下标 1 开始存储
	cin >> s + 1;
	int n = strlen(s + 1);
	for(int i = 1; i <= n; i ++)
		l[i] = l[i - 1] + (s[i] == 'T');
	for(int i = n; i >= 1; i --)
		r[i] = r[i + 1] + (s[i] == 'T');
	long long ans = 0;
	for(int i = 1; i <= n; i ++)
        /// 一定要将类型转化为 longlong 极限情况下 int 乘法会溢出
		ans += 1ll * (s[i] == '^') * l[i] * r[i];
	cout << ans;
  return 0;
}

算法分析

本算法的时间复杂度为 O ( n ) O(n) O(n)

解法二

思路

反向来思考,对于每一个 T( i ∈ [ 1 , n ] i\in[1,n] i[1,n])。若区间 [ 1 , i − 1 ] [1,i-1] [1,i1] 中有 x x x 个不同的 T^ 子序列,那么其对答案的贡献即为 x x x

现在子问题转化为求 T^ 子序列的个数,同样的思考,对于每一个 ^ 字符( j ∈ [ 1 , n ] j\in[1,n] j[1,n])。若区间 [ 1 , j − 1 ] [1,j-1] [1j1] 中有 y y y 个不同的 T^ 子序列, z z z 个不同的 T 子序列(T 出现的个数),那么区间 [ 1 , j ] [1,j] [1,j] 中 T^ 子序列的个数即为 y + z y+z y+z

可以注意到整个过程就是一个递推的关系(详见代码逻辑),故直接递推即可。

代码

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1000009;
char s[N];

int main() {
	cin >> s + 1;
	int n = strlen(s + 1);
	/// 由于标识符命名规则的限制,下面命名中用字符 _ 代替字符 ^
	long long T = 0, T_ = 0, T_T = 0;
	for(int i = 1; i <= n; i ++) {
		if(s[i] == 'T') {
			T ++;
			T_T += T_;
		} else if(s[i] == '^') {
			T_ += T;
		}
	}
	cout << T_T;
  return 0;
}

算法分析

本算法的时间复杂度为 O ( n ) O(n) O(n)

  • 17
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值