3388. 统计数组中的美丽分割(dp动态规划或者字符串哈希)
https://leetcode.cn/problems/count-beautiful-splits-in-an-array/description/
给你一个整数数组\(nums\)。
如果数组\(nums\)的一个分割满足以下条件,我们称它是一个 美丽 分割:
- 数组\(nums\)分为三段非空子数组:\(nums_1\) ,\(nums_2\) 和 \(nums_3\),三个数组\(nums_1\),\(nums_2\)和\(nums_3\) 按顺序连接可以得到 nums。
- 子数组\(nums_1\)是子数组\(nums_2\)的 前缀 或者\(nums_2\)是\(nums_3\)的 前缀。
请你返回满足以上条件的分割 数目 。
子数组 指的是一个数组里一段连续 非空 的元素。
前缀 指的是一个数组从头开始到中间某个元素结束的子数组。
示例 1:
输入:nums = [1,1,2,1]
输出:2
解释:
美丽分割如下:
nums1 = [1] ,nums2 = [1,2] ,nums3 = [1] 。
nums1 = [1] ,nums2 = [1] ,nums3 = [2,1] 。
示例 2:
输入:nums = [1,2,3,4]
输出:0
解释:
没有美丽分割。
提示:
1 <= nums.length <= 5000
0 <= nums[i] <= 50
首先这个题看到应该就要想起来字符串哈希,就是O(n^2)枚举第二个和第三个开始。
枚举i,j。就是第一个为[0,i-1],第二个为[i,j-1],第三个是[j,n-1].
解法一:字符串华哈希
typedef unsigned long long ull;
const int N=1e4+10,P=131;
class Solution {
ull p[N],h[N];
void init(vector<int>& s){
int n=s.size();
p[0]=1;
for(int i=1;i<=n;i++){
h[i]=h[i-1]*P+s[i-1];
p[i]=p[i-1]*P;
}
}
ull get(int l,int r){
return h[r+1]-h[l]*p[r-l+1];
}
bool ISpre(int l1,int r1,int l2,int r2){
int len1=r1-l1+1;
int len2=r2-l2+1;
if(len1>len2) return false;
return get(l1,r1)==get(l2,l2+len1-1);
}
public:
int beautifulSplits(vector<int>& nums) {
int n=nums.size();
if(n<3) return 0;
init(nums);
int count=0;
for(int i=1;i<n-1;i++){//枚举第二个开始
for(int j=i+1;j<n;j++){//第三个开始
if(ISpre(0,i-1,i,j-1)||ISpre(i,j-1,j,n-1)){
count++;
}
}
}
return count;
}
};
解法二:
定义 lcp[i][j] 表示后缀 nums[i:] 和后缀 nums[j:] 的最长公共前缀(Longest Common Prefix)的长度。
对于这个lcp[i][j],就是比如说字符串为abcabcd,那么dp[1][1]=7,dp[1][4]=3.
class Solution {
public:
int beautifulSplits(vector<int>& nums) {
int n=nums.size();
vector f(n+1,vector<int>(n+1));
for(int i=n-1;i>=0;i--){
for(int j=n-1;j>=i;j--){
if(nums[i]==nums[j]){
f[i][j]=f[i+1][j+1]+1;
}
}
}//f[0][0]=4
int ans=0;
for(int i=0;i<n;i++){
for(int j=i+1;j+1<n;j++){
int len1=i+1,len2=j-i,len3=n-1-j;
if((len1<=len2&&f[0][i+1]>=len1)||(len2<=len3&&f[i+1][j+1]>=len2)) ans++;
}
}
return ans;
}
};