1.算法
继续学习了状态机模型,难度加大,尤其是与kmp结合后难度更上一层楼。
该题与股票买卖4的区别在于去除了最多进行k次交易的限制,可以进行任意次交易并且一旦卖出后至少过一天才可以买入,既然如此我们的状态表示可以表示为第i天手上无/有货且是/不是冷冻期,状态转移方程根据题意来,分别考虑由无货转移过来还是由有货转移,并且手上无货的时候还可能处于冷冻期的时候,这点要与不处于冷冻期分开来写。
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 2e5 + 10,inf = 0x3f3f3f3f,mod = 1e9 + 7;
int n;
int a[N];
int dp[N][2][2];
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
memset(dp , -inf , sizeof dp);
for(int i = 1 ; i <= n ; i++)
{
cin>>a[i];
dp[i][0][0] = 0;
}
dp[0][0][0] = 0;
for(int i = 1 ; i <= n ; i++)
{
dp[i][0][0] = max(dp[i][0][0] , max(dp[i - 1][0][0] , dp[i - 1][0][1]));
dp[i][0][1] = max(dp[i][0][1] , dp[i - 1][1][0] + a[i]);
dp[i][1][0] = max(dp[i][1][0] , max(dp[i - 1][1][0] , dp[i - 1][0][0] - a[i]));
}
int maxn = 0;
for(int i = 0 ; i <= 1 ; i++)
{
for(int j = 0 ; j <= 1 ; j++)
{
maxn = max(maxn , dp[n][i][j]);
}
}
cout<<maxn;
}
例题2. 141. 周期 - AcWing题库
重新复习了一遍kmp算法,加深了一些理解,首先我们需要知道next数组的含义,next[i]数组的含义是对于字符串s[1 ~ i]的最大公共前后缀的长度,在进行字符串匹配的时候我们的j其实是在重复j++与j = next[j](回跳)的过程,若新的字符能够匹配那么就j++,不然j会一直往回跳,跳到当前b[1 ~ j]最长后缀相等的最长前缀的结尾,重复这个操作,这样最坏的情况也不过是从第一个字符重新匹配。这道题目主要是考察了对next数组的理解,我们通过kmp模板求出给定字符串的next数组后要去找是否存在a[1 ~ i]且1~i是由若干循环节组成的,那么就可以想到最少也是由两个循环节组成,拥有了两个循环节后我们就可以递推由x个循环节组成的前缀是否存在,那么由两个循环节组成的前缀必定满足i == 2 * ne[i],那么我们只要找到满足该条件的i后就可以往后拓展,此时已知的是循环节的长度就是ne[i],那么我们只需要令i += ne[i]就是期望的由3个循环节组成的前缀,长度满足了那么如何确定是不是由循环节组成呢,我们可以发现ne数组会相应增加ne[i]那么只要每次i + ne[i],然后看ne[i + ne[i]]是否 == ne[i] + ne[i],直到不满足为止
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 1e6 + 10,inf = 0x3f3f3f3f,mod = 1e9 + 7;
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
for(int i = 1 ; ; i++)
{
int n;
string s;
cin>>n;
if(n == 0)
{
return 0;
}else
{
cin>>s;
s = " " + s;
cout<<"Test case #"<<i<<"\n";
vector <int> ne(n + 10 , 0),st(n + 10 , 0);
vector <pii> ans;
for(int i = 2,j = 0 ; i <= n ; i++)
{
while(j && s[i] != s[j + 1])
{
j = ne[j];
}
if(s[i] == s[j + 1])
{
j++;
}
ne[i] = j;
}
for(int i = 1 ; i <= n ; i++)
{
// cout<<ne[i]<<" ";
if(!st[i])
{
st[i] = 1;
if(i == 2 * ne[i])
{
ans.push_back({i , i / ne[i]});
int j = i + ne[i],k = ne[i] * 2;
while(j <= n)
{
st[j] = 1;
if(ne[j] == k)
{
ans.push_back({j , j / ne[i]});
j += ne[i],k += ne[i];
}else
{
break;
}
}
}else
{
continue;
}
}
}
// cout<<"\n";
// sort(ans.begin() , ans.end() , [&](pii a,pii b){return a.x < b.x;});
for(int i = 0 ; i < ans.size() ; i++)
{
cout<<ans[i].x<<" "<<ans[i].y<<"\n";
}
cout<<"\n";
}
}
}
这道题难度我觉得非常大,是状态机模型和kmp匹配的结合,到现在还没有完全理解,首先考虑我们的状态机的模型,我们设计的每一位密码都有26个节点,而我们必须保证我们的密码不能与t串匹配,那么就是说我们的密码方案数就是可以与t串的1 - m-1位匹配或者为0(不匹配)的情况的总数,这也确实涵盖了所有的情况,我们的dp建立一个二维的状态表示的是设计好的第i位密码与t串的第j位匹配的方案数,我们先求出t串的ne数组,dp[0][0]代表已经创造0位密码与t串不匹配的方案数,一定只有1个,作为初始化,接下来是三层循环第一层枚举已经设计好i位,第二层循环是枚举这i位密码与t串的j位匹配,第三重循环是26个字母代表我们每个节点有26个路径,之后我们对进行kmp匹配检查新加的字符后原来的字符串会跟t串的第几位匹配,若不会与t完全匹配即u!=m那么就可以进行转移,dp[i + 1][u] += dp[i][j],最后求总的方案数是将设计好的n位密码与t串除了完全匹配之外的所有匹配状态全部累加起来即为最终的答案
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 2e5 + 10,inf = 0x3f3f3f3f,mod = 1e9 + 7;
int n;
string s;
int ne[N];
ll dp[100][100];
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>s;
int m = s.size();
s = " " + s;
for(int i = 2,j = 0 ; i <= m ; i++)
{
while(j && s[i] != s[j + 1])
{
j = ne[j];
}
if(s[i] == s[j + 1])
{
j++;
}
ne[i] = j;
}
dp[0][0] = 1;
for(int i = 0 ; i < n ; i++)
{
for(int j = 0 ; j < m ; j++)
{
for(char c = 'a' ; c <= 'z' ; c++)
{
int u = j;
while(u && s[u + 1] != c)
{
u = ne[u];
}
if(s[u + 1] == c)
{
u++;
}
if(u < m)
{
dp[i + 1][u] = (dp[i + 1][u] + dp[i][j]) % mod;
}
}
}
}
ll ans = 0;
for(int i = 0 ; i < m ; i++)
{
ans = (ans + dp[n][i]) % mod;
}
cout<<ans;
}
明天开始学习区间dp。
2.web板块
css相当于对网页的自定义装修,我们有三种定义方式,分别是行内样式表,即直接在标签内写css,效果只对该标签生效
<img src="/img/pqj.jpg" alt="" style="width:300px;height:200px">
另一种是内部样式表,就是在html文件内的head部分直接写css,从而对该文件内的对应区域生效
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=img, initial-scale=1.0">
<title>Document</title>
<style type="text/css">
img {
width: 300px;
height: 300px;
/* border-radius: 50%; */
}
P {
width: 300px;
height: 300px;
border-radius: 50%;
}
.blue-p {
background-color: blue
}
.green-p {
background-color: green;
}
.big {
width: 400px;
height: 400px;
}
</style>
<link rel="icon" href="/img/human.jpg">
</head>
<body>
<img src="/img/pqj.jpg" alt="" style="width:300px;height:200px">
<img src="/img/hjj.jpg" alt="">
<div>wst</div>
<P class="blue-p">1</P>
<P class="green-p big">2</P>
<P class="blue-p">3</P>
<P class="green-p">4</P>
</body>
</html>
以及最后一种也就是外部样式表,就是将我们的css封装在一个css文件内,只需要在想要添加该css文件的html文件内写入该css的路径即可生效
此外还有不同的选择器,例如标签选择器和类选择器,标签选择器是对一类标签进行修改而类选择器是对自己进行标签的板块进行修改
P {
width: 300px;
height: 300px;
border-radius: 50%;
}/*标签选择器*/
.big {
width: 400px;
height: 400px;
}/*类选择器*/