将连续问题转化成点的问题
统计和小于目标的下标对数目
给你一个下标从 0 开始长度为
n
的整数数组nums
和一个整数target
,请你返回满足0 <= i < j < n
且nums[i] + nums[j] < target
的下标对(i, j)
的数目。
枚举方法:
class Solution {
public:
int countPairs(vector<int>& nums, int target) {
int res = 0;
//枚举所有的下标对
for (int i = 0; i < nums.size(); i++) {
for (int j = i + 1; j < nums.size(); j++) {
if (nums[i] + nums[j] < target) {
res++;
}
}
}
return res;
}
};
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
其中
n
n
n 表示数组
n
u
m
s
nums
nums 的长度。一共最多有
n
2
n^2
n2 个下标对,因此遍历所有的下标对需要的时间为
O
(
n
2
)
O(n^2)
O(n2)
这个复杂度略显大了,难以应对大量数据,因此需要给出更快的方法
思路:
与枚举每个下标对这种连续的问题相比,不如换个思路,先定下来一个点nums[i]
然后将问题转化成去寻找nums[j]<target-nums[i]
的个数,这时候很显然,利用排序升序排序加二分查找的方式找到第一个大于等于target-nums[i]
的下标pos
便是我们需要的个数。
代码:
//这个代码放力扣上提交能有0ms的运行时间!!!
class Solution {
public:
int countPairs(vector<int>& nums, int target) {
int cnt=0;
sort(nums.begin(), nums.end());
for (int i=0; i<nums.size();i++){
int lim=target-nums[i];
//二分查找
int l=0,r=nums.size()-1,pos=-1;
while (l<=r){
int mid=(l+r)>>1;
if (nums[mid]<lim) pos=mid, l=mid+1;
else r=mid-1;
}
if (pos!=-1) cnt+=pos+1;
}
for (int i=0; i<nums.size(); i++)
if (nums[i]+nums[i]<target) cnt--;
return cnt/2;
}
};
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn),其中 n n n 表示数组 nums 的长度。排序需要的时间复为 O ( n l o g n ) O(nlogn) O(nlogn),每次进行二分查找需要的时间为 $O(logn) $ ,一共需要进行 n n n 次二分查找,因此总的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn) 。
统计子串中的唯一字符
我们定义了一个函数
countUniqueChars(s)
来统计字符串s
中的唯一字符,并返回唯一字符的个数。例如:
s = "LEETCODE"
,则其中"L"
,"T"
,"C"
,"O"
,"D"
都是唯一字符,因为它们只出现一次,所以countUniqueChars(s) = 5
。本题将会给你一个字符串
s
,我们需要返回countUniqueChars(t)
的总和,其中t
是s
的子字符串。输入用例保证返回值为 32 位整数。注意,某些子字符串可能是重复的,但你统计时也必须算上这些重复的子字符串(也就是说,你必须统计
s
的所有子字符串中的唯一字符)。
思路:
假如枚举,需要连续不断地列举完所有子集,是一个连续的问题,时间复杂度为 n 2 n^2 n2 ,当 n n n 数量级比较大时,计算时间过长,需要简化计算。
我们可以通过**分别计算每个字符的“贡献”**的方法,将这个问题转化成每个点的问题,假设对某个下标为k的位置的字母,这个字母上一次出现的位置是i
,下一次是j
,那么从k
开始,往左在[i+1,k]
的区间内取子集的左边界,往右在[k,j-1]
取子集的右边界,这些子集均包含s[k]
这个”唯一字符“,换句话说s[k]
给这些子集都“贡献”了
1
1
1个countUniqueChars
,因此,总的uniqueLetterString
增加了子集总数(mid-pre+1)*(nxt-mid+1)
,因此只要枚举并计算每一个字母的“贡献”,即可得到答案。
代码:
class Solution {
public:
int uniqueLetterString(string s) {
vector <int> pos[26];
for (int i = 0; i < s.length(); i++) pos[s[i]-'A'].emplace_back(i);
int ans = 0;
for (char now=0; now<26;now++)
for (int i=0;i < pos [now].size() ;i++){
int pre,nxt,mid=pos[now][i];
if (i==0) pre=0;
else pre=pos[now][i-1]+1;
if (i==pos[now].size()-1) nxt=s.length()-1;
else nxt=pos[now][i+1]-1;
ans+=1ll*(mid-pre+1)*(nxt-mid+1);
}
return (int)ans;
}
};