状压DP问题
1.2021-4-15 用异或性质判断 奇偶性质
题目:
给你一个字符串 s ,请你返回满足以下条件的最长子字符串的长度:每个元音字母,即 ‘a’,‘e’,‘i’,‘o’,‘u’ ,在子字符串中都恰好出现了偶数次。
示例1
输入
s = "eleetminicoworoep"
输出
13
解释
最长子字符串是 "leetminicowor" ,它包含 e,i,o 各 2 个,以及 0 个 a,u 。
思路:
非常巧妙的一道题。
用了异或的性质:00001^00001=00000,00000^00001=00001
00010^00010=00000。
即一个二进制数 某个位如果 异或 当前位为1,其它位为0的数,那么会从1->0,从0->1。
这种性质刚好符合了奇数+1=偶数,偶数+1=奇数的性质。
如果当前题目是统计a为偶数时最长子字符串。
那么只需要通过一个dp[2]来记录dp[1]时的下标和dp[0]时的下标,每次更新一下下标差求最大值。
其status只需要用到长度为1的二进制数即可。
那么题目要求5个字符,设置status为长度为5的二进制数即可。status=00000初始。
每次和00001 00010 00100 01000 10000进行异或。
注意一个细节dp[0]=0而不是-1,这是因为00000是默认已经出现过的。
还有坐标通过i+1来存,这是因为下标差与实际长度差1。
代码:
class Solution {
public:
int findTheLongestSubstring(string s) {
int n=s.size();
vector<int> dp(32,-1);
dp[0]=0;//00000出现了一次
int status=0;
int ans=0;
for(int i=0;i<n;++i){
char c=s[i];
if(c=='a')status^=(1<<0);
else if(c=='e')status^=(1<<1);
else if(c=='i')status^=(1<<2);
else if(c=='o')status^=(1<<3);
else if(c=='u')status^=(1<<4);
if(dp[status]!=-1)ans=max(ans,i+1-dp[status]);
else dp[status]=i+1;
//注意status保存成i+1 完全是因为索引差和长度差11
}
return ans;
}
};
2.2021-4-15 判断连续1 相邻1
思路:
首先是可以种植的所有状态个数为SUM 判断依据为避免行相邻 i&(i<<1)==0
其次是自己行不能种植的格要空开,即cant[i]+=(1<<(N-j)) if tmp==0
某个状态是否与另一个状态在同一个位上有相同的1,即i&j!=1即可
特别需要注意的一点是int status[SUM]存放了各个状态,而1~SUM只是状态的编号
dp[i][j]为第i行第j种状态可以选择的所有方案数
dp[i][j]==dp[i-1][k1]+dp[i-1][k2]+...+dp[i-1][ksum]
首先是j&cant[i]!=1
然后dp[i-1[k:1~NUM] k&j!=1 k&cant[i-1]!=1
代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<stdio.h>
#include<string.h>
using ll = long long;
using namespace std;
int M, N;
int SUM;
int cant[13];
int status[5000];
const int MOD = 100000000;
void Init() {
SUM = 0;
for (int i = 0; i < (1 << N); ++i) {
if ((i & (i << 1)) == 0)status[++SUM] = i;
}
}
bool Judge(int i, int j) {
if (i& j)return false;
else return true;
}
int main() {
while (cin >> M >> N) {
memset(cant, 0, sizeof(cant));
memset(status, 0, sizeof(status));
Init();
for (int i = 1; i <= M; ++i) {
for (int j = 1; j <= N; ++j) {
int tmp; cin >> tmp;
if (tmp == 0)cant[i] += (1 << (N - j));
}
}
vector<vector<int> > dp(M + 1, vector<int>(SUM+1, 0));
for (int j = 1; j <= SUM; ++j) {
if (Judge(status[j], cant[1]))dp[1][j] = 1;
}
for (int i = 2; i <= M; ++i) {
for (int j = 1; j <= SUM; ++j) {
if (!Judge(status[j], cant[i]))continue;
for (int k = 1; k <= SUM; ++k) {
if (!Judge(status[k], status[j]))continue;
if (!Judge(status[k], cant[i - 1]))continue;
dp[i][j] = (dp[i - 1][k]+ dp[i][j])%MOD;
}
}
}
int ans = 0;
for (int j = 1; j <= SUM; ++j) {
ans = (ans+dp[M][j])%MOD;
}
cout << ans << endl;
}
return 0;
}
一维DP问题
1.2021-4-4 字符串字符结合分析
题目:
已知摩尔斯电码和字符映射关系如下:
A -> 0
B -> 1
C -> 10
D -> 11
E -> 100
F -> 101
G -> 110
H -> 111
当前我们获取到了一串01数字字符串,需要进行摩尔斯电码解码,请问共有多少种解码方法?
输入描述:
一行由0和1组成的字符串
输出描述: