一、旋转字符串(左旋字符串)


cpp
class Solution {
public:
bool solve(string A, string B) {
if(A.size()!=B.size()) return false;
return (A+A).find(B)!=-1;
}
};
二、**合并k个已排序的链表(重写堆/归并排序)


解法1:利用堆
cpp
#include <queue>
class Solution {
public:
struct cmp{
bool operator()(ListNode*l1,ListNode*l2){//注意 小根堆是大于
return l1->val>l2->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<ListNode*,vector<ListNode*>,cmp> heap;//小根堆
for(auto&head:lists)
if(head) heap.push(head);
ListNode* newhead=new ListNode(0);//哨兵头节点
ListNode*ptail=newhead;//用来尾插
while(!heap.empty()){
auto t=heap.top();
heap.pop();
ptail=ptail->next=t;
if(t->next) heap.push(t->next);
}
ListNode*ret=newhead->next;
delete newhead;
return ret;
}
};
解法2:利用归并排序(面试经典)
cpp
class Solution {
public:
//利用归并排序
ListNode* mergeKLists(vector<ListNode*>&lists) {
return mergesort(lists,0,lists.size()-1);
}
ListNode* mergesort(vector<ListNode*>&lists,int left,int right){
if(left>right) return nullptr;
else if(left==right) return lists[left];//只有一个的情况
int mid=(left+right)>>1;
ListNode*cur1=mergesort(lists,left,mid);
ListNode*cur2=mergesort(lists,mid+1,right);
//加一个哨兵头结点
ListNode* newhead = new ListNode(0);
ListNode* ptail=newhead;//用来尾插
while(cur1&&cur2){
if(cur1->val<=cur2->val){
ptail->next=cur1;
cur1=cur1->next;
}
else{
ptail->next=cur2;
cur2=cur2->next;
}
ptail=ptail->next;
}
//还有剩的就直接接在后面
if(cur1) ptail->next=cur1;
else ptail->next=cur2;
ListNode*ret=newhead->next;
delete newhead;
return ret;
}
};
三、滑雪(记忆化搜索)


cpp
#include <iostream>
using namespace std;
const int N=110;
int arr[N][N];
int memo[N][N];//记忆数组
int dx[4]={0,0,1,-1};
int dy[4]={-1,1,0,0};
int n,m;
int dfs(int i,int j){
if(memo[i][j]) return memo[i][j];
int ret=1;
for(int k=0;k<4;++k){
int x=dx[k]+i,y=dy[k]+j;
if(x>=1&&x<=n&&y>=1&&y<=m&&arr[x][y]<arr[i][j])
ret=max(ret,1+dfs(x,y));
}
return memo[i][j]=ret;
}
int main() {
//严格单调递减
cin>>n>>m;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) cin>>arr[i][j];
int ret=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
ret=max(ret,dfs(i,j));
cout<<ret<<endl;
}
四、天使果冻(递推)


直接用两个变量就行了
cpp
#include<iostream>
using namespace std;
int n,q,x;
const int N=1e5+10;
int max1,max2;//前面是最大 后面是次大
int arr[N];
int main(){
cin>>n;
cin>>max1;
for(int i=2;i<=n;++i){
cin>>arr[i];
if(arr[i]>=max1){
max2=max1;
max1=arr[i];
}
else if(arr[i]>max2) max2=arr[i];
arr[i]=max2;
}
cin>>q;
while(q--){
cin>>x;
cout<<arr[x]<<endl;
}
}
五、**dd爱旋转(矩阵旋转规律+优化模拟)

如果每次都去尝试旋转遍历,那么时间复杂度是q*n^2
但是利用下面"消消乐"性质,时间复杂度就变成了q+n^2

cpp
#include<iostream>
using namespace std;
int n,q,x;
const int N=1010;
int arr[N][N];
int row,col;//行变化和列变化的次数
void setrow(){//行变换
for(int i=0;i<n/2;++i)
for(int j=0;j<n;++j)
swap(arr[i][j],arr[n-i-1][j]);
}
void setcol(){//列变换
for(int i=0;i<n;++i)
for(int j=0;j<n/2;++j)
swap(arr[i][j],arr[i][n-j-1]);
}
int main(){
cin>>n;
for(int i=0;i<n;++i)
for(int j=0;j<n;++j) cin>>arr[i][j];
cin>>q;
while(q--){
cin>>x;
if(x==1) ++col;
++row;
}
if(row%2) setrow();
if(col%2) setcol();
for(int i=0;i<n;++i){
for(int j=0;j<n;++j)
cout<<arr[i][j]<<" ";
cout<<endl;
}
}
六、**小红取数(01背包+同余定理)

无法空间优化

cpp
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N=1010;
LL arr[N];
LL dp[N][N];//dp[i][j]表示从前i个人选 总和%k==j时的最大和是多少
int n,k;
int main() {
cin>>n>>k;
memset(dp, -0x3f, sizeof dp);
dp[0][0] = 0;
for(int i=1;i<=n;++i) cin>>arr[i];
for(int i=1;i<=n;++i)
for(int j=0;j<k;++j)
dp[i][j]=max(dp[i-1][j],dp[i-1][(j-arr[i]%k+k)%k]+arr[i]);
if(dp[n][0]<=0) cout<<-1<<endl;
else cout<<dp[n][0]<<endl;
}
七、神奇的字母2(哈希)

cpp
#include<iostream>
#include<string>
using namespace std;
string s;
int main(){
int hash[26]={0};
char ret=0;
int maxcount=0;
while(cin>>s)
for(auto&ch:s)
if(++hash[ch-'a']>maxcount){
maxcount=hash[ch-'a'];
ret=ch;
}
cout<<ret<<endl;
}
八、字符编码(哈夫曼编码)


cpp
#include <iostream>
#include <queue>
#include <string>
#include <vector>
using namespace std;
string s;
int main() {
while(cin>>s){
//先统计每个字符的频次 然后再丢到小根堆里
int hash[128]={0};
for(auto&ch:s) ++hash[ch];
//将频次丢到小根堆里
priority_queue<int,vector<int>,greater<int>> heap;
for(int i=0;i<128;++i)
if(hash[i]) heap.push(hash[i]);
int ret=0;
while(heap.size()>1){
int t1=heap.top();
heap.pop();
int t2=heap.top();
heap.pop();
ret+=t1+t2;
heap.push(t1+t2);
}
cout<<ret<<endl;
}
}
九、最少的完全平方数(完全背包)


cpp
#include <iostream>
#include <cstring>
using namespace std;
//完全背包问题
int n;
const int N=1e4+10;
int dp[N];
int main() {
cin>>n;
memset(dp,0x3f,sizeof dp);
dp[0]=0;
for(int i=1;i*i<=n;++i)
for(int j=i*i;j<=n;++j)
dp[j]=min(dp[j],dp[j-i*i]+1);
cout<<dp[n]<<endl;
}
十、*游游的字母串(优化枚举)


cpp
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
string s;
int main(){
cin>>s;
int ret=0x3f3f3f3f;
for(char i='a';i<='z';++i){
int sum=0;//统计变化的次数
for(auto&ch:s)
sum+=min(abs(ch-i),26-abs(ch-i));
ret=min(sum,ret);
}
cout<<ret<<endl;
}
十一、体育课测试2(拓扑排序)


cpp
class Solution {
public:
vector<int> findOrder(int numProject, vector<vector<int>>& groups) {
vector<vector<int>> edges(numProject);//邻接表
vector<int> in(numProject);//入度表
vector<int> ret;//结果集
ret.reserve(numProject);//提前扩容
for(auto&v:groups){//开始建边 然后填入度表
int a=v[0],b=v[1];
edges[b].emplace_back(a);
++in[a];
}
//开始遍历入度表 然后把度为0的插入到队列中
queue<int> q;
for(int i=0;i<numProject;++i)
if(!in[i]) q.emplace(i);
//开始做事
while(!q.empty()){
int a=q.front();
q.pop();
ret.emplace_back(a);
//把所有跟t有关的入度都--
for(auto b:edges[a])
if(--in[b]==0) q.emplace(b);
}
if(ret.size()==numProject) return ret;
return {};
}
};
十二、合唱队形(序列dp)


cpp
#include <iostream>
using namespace std;
const int N=1010;
int arr[N];//身高数组
int f[N],g[N];//最长递增子序列
//f是从前往后 以i位置为结尾时的最长递增子序列
//g是从后往前 以i位置为结尾时的最长递增子序列
int n;
int main() {
cin>>n;
for(int i=1;i<=n;++i) cin>>arr[i];
//从前往后
for(int i=1;i<=n;++i){
f[i]=1;
for(int j=1;j<i;++j)
if(arr[j]<arr[i]) f[i]=max(f[i],f[j]+1);
}
//从后往前
for(int i=n;i>=1;--i){
g[i]=1;
for(int j=i+1;j<=n;++j)
if(arr[j]<arr[i]) g[i]=max(g[i],g[j]+1);
}
int ret=1;
for(int i=1;i<=n;++i)
ret=max(ret,f[i]+g[i]-1);
cout<<n-ret<<endl;
}
十三、棋子翻转(模拟)

cpp
class Solution {
public:
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
vector<vector<int>> flipChess(vector<vector<int>>& A, vector<vector<int>>&f) {
for(auto&v:f){
int a=v[0]-1,b=v[1]-1;
for(int k=0;k<4;++k){
int x=dx[k]+a,y=dy[k]+b;
if(x>=0&&x<4&&y>=0&&y<4) A[x][y]^=1;
}
}
return A;
}
};
十四、**宵暗的妖怪(类似打家劫舍实际不是)


cpp
//打家劫舍问题 但是不能抢到最后一家
#include<iostream>
using namespace std;
typedef long long LL;
const int N=1e5+10;
LL arr[N],dp[N];
//dp[i]表示1-i区间的最大饱食度是多少
//dp[i-3]+arr[i]
int n;
int main(){
cin>>n;
for(int i=1;i<=n;++i) cin>>arr[i];
for(int i=3;i<=n;++i)
dp[i]=max(dp[i-1],dp[i-3]+arr[i-1]);
cout<<dp[n]<<endl;
}
十五、**过桥(贪心+双指针+层序遍历思想)


cpp
#include<iostream>
using namespace std;
int n;
int arr[2010];
int bfs(){
int left=1,right=1,maxpos=1,ret=0;
while(left<=right){
if(maxpos>=n) return ret;
for(int i=left;i<=right;++i)
maxpos=max(maxpos,arr[i]+i);
left=right+1;
right=maxpos;
++ret;
}
return -1;
}
int main(){
cin>>n;
for(int i=1;i<=n;++i) cin>>arr[i];
cout<<bfs()<<endl;
}
十六、最大差值

这题类似之前遇到的股票问题1
cpp
class Solution {
public:
int getDis(vector<int>& A, int n) {
int ret=0;//记录最大差值
int prevmin=A[0];//记录在这之前的最小值
for(int i=1;i<n;++i){
ret=max(ret,A[i]-prevmin);
prevmin=min(prevmin,A[i]);
}
return ret;
}
};
十七、兑换零钱(完全背包)


cpp
#include <iostream>
#include <cstring>
using namespace std;
//dp[i][j]表示从前i个数选 面值恰好为j的最小张数
const int N=1010,M=5010;
int n,aim;
int arr[N],dp[M];
int main() {
cin>>n>>aim;
memset(dp,0x3f,sizeof dp);
dp[0]=0;
for(int i=1;i<=n;++i) cin>>arr[i];
for(int i=1;i<=n;++i)
for(int j=arr[i];j<=aim;++j)
dp[j]=min(dp[j],dp[j-arr[i]]+1);
if(dp[aim]>=0x3f3f3f3f) cout<<-1<<endl;
else cout<<dp[aim]<<endl;
}
十八、**小红的子串(滑动窗口+前缀和)


cpp
#include<string>
#include<iostream>
using namespace std;
typedef long long LL;
int n,l,r;
string s;
LL find(int x){//1-x个种类的数量 用滑动窗口
if(x==0) return 0;
LL ret=0;
int hash[26]={0},kinds=0;
//滑动窗口
for(int left=0,right=0;right<n;++right){
if(hash[s[right]-'a']++==0) ++kinds;
while(kinds>x)
if(--hash[s[left++]-'a']==0) --kinds;
ret+=right-left+1;
}
return ret;
}
int main(){
cin>>n>>l>>r>>s;
cout<<find(r)-find(l-1)<<endl;
}
