笔记3
生成树并查集的路径压缩
点击查看代码
//O(mlogm) sort最慢
#include<bits/stdc++.h>
using namespace std;
int to[MAXN];//to[i] 表示 i 点在并查集里面的箭头指向谁
int go(int p){//看一下点 p 沿着并查集箭头最后会走到哪里
//O(1)
if(to[p]==p)return p;//指向自己
/*else{
int q=go(to[p]);
to[p]=q;
return q;
}*/
else return to[p]=go(to[p]);
}
struct edge{
int s,e,d;
}ed[MAXN];//ed[i] 代表第 i 条边是在 s 与 e 之间的长度为 d 的边
int n,m;
bool cmp(edge a,edge b){
return a.d<b.d;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>ed[i].s>>ed[i].e>>ed[i].d;
sort(ed+1,ed+m+1,cmp);//所有边按照边权进行排序
for(int i=1;i<=n;i++)
to[i]=i;//初始化并查集
int ans=0;//生成树大小
int cnt=0;//边的数量
for(int i=1;i<=m;i++){//O(m)
if(cnt==n-1)break;
int p1=ed[i].s,p2=ed[i].e,d=ed[i].d;
if(go(p1)!=go(p2)){//不在同一个连通块
ans+=d;
to[go(p1)]=go(p2);
++cnt;
}
}
cout<<ans<<endl;
}
SPFA
点击查看代码
#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int> > z[500005];
int dist[500005];//dist[i] 代表从起点到 i 的最短路
bool vis[500005];//vis[i]代表 i 在不在队列里
void add_edge(int s,int e,int d){
z[s].push_back(make_pair(e,d));
}
int n,m,x;
void SPFA(int s){//以 s 作为起点算最短路
memset(dist,0x3f,sizeof(dist));
dist[s]=0;
queue<int> q;//用来存可能改变其他点最短路的点
q.push(s);
vis[s]=true;
while(!q.empty()){
int p=q.front();
q.pop();
vis[p]=false;
for(int i=0;i<z[p].size();i++){
int e=z[p][i].first;
int d=z[p][i].second;
if(dist[e]>dist[p]+d){
dist[e]=dist[p]+d;
if(!vis[e])q.push(e),vis[e]=true;
}
}
}
}
signed main(){
cin>>n>>m>>x;
for(int i=1;i<=m;i++){
int s,e,d;
cin>>s>>e>>d;
add_edge(s,e,d);
}
SPFA(x);
for(int i=1;i<=n;i++){
cout<<dist[i]<<' ';
}
return 0;
}
生成树
点击查看代码
//O(nm)
#include<bits/stdc++.h>
using namespace std;
int to[MAXN];//to[i] 表示 i 点在并查集里面的箭头指向谁
int go(int p){//看一下点 p 沿着并查集箭头最后会走到哪里
if(to[p]==p)return p;//指向自己
else return go(to[p]);//递归调用
}
struct edge{
int s,e,d;
}ed[MAXN];//ed[i] 代表第 i 条边是在 s 与 e 之间的长度为 d 的边
int n,m;
bool cmp(edge a,edge b){
return a.d<b.d;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>ed[i].s>>ed[i].e>>ed[i].d;
sort(ed+1,ed+m+1,cmp);//所有边按照边权进行排序
for(int i=1;i<=n;i++)
to[i]=i;//初始化并查集
int ans=0;//生成树大小
int cnt=0;//边的数量
for(int i=1;i<=m;i++){
if(cnt==n-1)break;
int p1=ed[i].s,p2=ed[i].e,d=ed[i].d;
if(go(p1)!=go(p2)){//不在同一个连通块
ans+=d;
to[go(p1)]=go(p2);
++cnt;
}
}
cout<<ans<<endl;
}
Bellman_ford
点击查看代码
//可以针对负数边权,但是更慢。
#include<bits/stdc++.h>
using namespace std;
int s[100005],d[1000005],e[1000005];
int n,m;
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>s[i]>>e[i]>>d[i];
memset(dist,0x3f,sizeof(dist));
dist[1]=0;
for(int i=1;i<n;i++)
for(int j=1;j<=m;j++)
dist[e[j]]=min(dist[e[j]],dist[s[j]]+d[j]);
return 0;
}
Dijkstra优化
点击查看代码
//用堆
#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int> > z[100005];
int dist[100005];//dist[i] 代表从起点到 i 的最短路
bool vis[i];//vis[i]代表 i 的最短路有没有被求出来
void add_edge(int s,int e,int d){
z[s].push_back(make_pair(e,d));
}
int n,m;
void Dijkstra(int s){//以 s 作为起点算最短路
memset(dist,0x3f,sizeof(dist));
dist[s]=0;
priority_queue<pair<int,int> > heap;
//first 用来存距离的相反数
//second 用来存点的编号
for(int i=1;i<=n;i++)
heap.push(make_pair(-dist[i],i));
for(int i=1;i<=n;i++){
//选一个 dist 最小的点
while(vis[heap.top().second])
heap.pop();
int p=heap.top().second;
heap.pop();
vis[p]=true;
//用这个点去进行松弛操作
for(int j=0;j<z[p].size();j++){
int q=z[p][j].first;
int d=z[p][j].second;///这是一条从 p 到 q 长度为 d 的边
if(dist[q]>dist[p]+d){
dist[q]=dist[p+d];
heap.push(make_pair(-dist[q],q));
}
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int s,e,d;
cin>>s>>e>>d;
add_edge(s,e,d);
}
Dijkstra(1);
int T;
cin>>T;
while(T--){
int x;
cin>>x;
cout<<dist[x]<<'\n';
}
return 0;
}
拓扑排序
点击查看代码
#include <bits/stdc++.h>
using namespace std;
vector<int>a[1005];
int c[1005],b[1005],cnt,n;
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int k;
cin>>k;
while(k!=0)
{
int v=k;
a[i].push_back(v);
c[v]++;
cin>>k;
}
}
queue<int>q;
for(int i=1;i<=n;i++)
{
if(c[i]==0)
{
q.push(i);
}
}
while(q.size())
{
b[++cnt]=q.front();
for(int i=0;i<a[q.front()].size();i++)
{
c[a[q.front()][i]]--;
if(c[a[q.front()][i]]==0)
{
q.push(a[q.front()][i]);
}
}
q.pop();
}
if(cnt!=n)
{
cout<<"not topo";
return 0;
}
for(int i=1;i<=n;i++)
{
cout<<b[i]<<" ";
}
return 0;
}
多源最短路算法floyd原始版本
点击查看代码
//O(n^3) n<=250
#include<bits/stdc++.h>
using namespace std;
int dist[1005][1005][1005];
//dist[i][j][k] 代表从 j 走到 k 使得中间经过的节点编号 <=i 的情况下的最短路
const int INF=0x3f3f3f3f;
int n,m;//n 点 m 边
signed main(){
memset(dist,0x3f,sizeof(dist));//把数组每一个元素赋值为INF
cin>>n>>m;
for(int i=1;i<=m;i++){
int s,e,d;
cin>>s>>e>>d;//一条从 s 到 e 长度为 d 的边
dist[0][s][e]=min(dist[0][s][e],d);
}
for(int i=1;i<=n;i++)
dist[0][i][i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
dist[i][j][k]=min(dist[i-1][j][k],dist[i-1][j][i]+dist[i-1][i][k]);
int T;
cin>>T;
while(T--){
int i,j;
cin>>i>>j;
cout<<dist[n][i][j]<<'\n';
}
return 0;
}
多源最短路算法floyd压维
点击查看代码
//O(n^3) n<=250
//注意是无向图,所以存图时要存两次。
#include<bits/stdc++.h>
using namespace std;
int dist[1005][1005];
//dist[i][j][k] 代表从 j 走到 k 使得中间经过的节点编号 <=i 的情况下的最短路
const int INF=0x3f3f3f3f;
int n,m;//n 点 m 边
signed main(){
memset(dist,0x3f,sizeof(dist));//把数组每一个元素赋值为INF
cin>>n>>m;
for(int i=1;i<=m;i++){
int s,e,d;
cin>>s>>e>>d;//一条从 s 到 e 长度为 d 的边
dist[s][e]=min(dist[s][e],d);
}
for(int i=1;i<=n;i++)
dist[i][i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
dist[j][k]=min(dist[j][k],dist[j][i]+dist[i][k]);
int T;
cin>>T;
while(T--){
int i,j;
cin>>i>>j;
cout<<dist[i][j]<<'\n';
}
return 0;
}
单源最短路算法Dijkstra
点击查看代码
//选一个最短路已经求好的点,选的点一定是当前 dist 最小的点。
//进行松弛操作(用自己的最短路去更新自己周围的最短路)。
#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int> > z[100005];
int dist[100005];//dist[i] 代表从起点到 i 的最短路
bool vis[i];//vis[i]代表 i 的最短路有没有被求出来
void add_edge(int s,int e,int d){
z[s].push_back(make_pair(e,d));
}
int n,m;
void Dijkstra(int s){//以 s 作为起点算最短路
memset(dist,0x3f,sizeof(dist));
dist[s]=0;
for(int i=1;i<=n;i++){
//选一个 dist 最小的点
int p=0;
for(int j=1;j<=n;j++)
if(!vis[j]&&(p==0||dist[j]<dist[p]))p=j;
vis[p]=true;
//用这个点去进行松弛操作
for(int j=0;j<z[p].size();j++){
int q=z[p][j].first;
int d=z[p][j].second;///这是一条从 p 到 q 长度为 d 的边
dist[q]=min(dist[q],dist[p]+d);
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int s,e,d;
cin>>s>>e>>d;
add_edge(s,e,d);
}
Dijkstra(1);
int T;
cin>>T;
while(T--){
int x;
cin>>x;
cout<<dist[x]<<'\n';
}
return 0;
}
匈牙利算法
点击查看代码
//匈牙利算法代码
//匈牙利算法可用邻接矩阵和编表,优化用编表,不优化用邻接矩阵
//时间复杂度:O(n^3)
#include <bits/stdc++.h>
using namespace std;
bool z[maxn][maxn],vis[maxn];//z[i][j]代表左边第i个点和右边第j个点能不能匹配 vis[i]代表右边第i个点在这一轮中有没有被请求匹配过
int n,m,k,result[maxn],ans;//n:左边有n个点,m:右边有m个点,k:中间有r条边,result[i]代表右边第i个点和左边第result[i]个点匹配
bool dfs(int i)//让左边第i个点尝试匹配,返回是否成功
{
for(int j=1;j<=m;j++)//让左边的第i个点和右边第j个点尝试匹配
{
if(z[i][j]&&!vis[j])
{
vis[j]=true;
if(!result[j]||dfs(result[j]))
{
result[j]=i;
return true;
}
}
}
return false;
}
signed main()
{
cin>>n>>m>>k;
for(int i=1;i<=k;i++)
{
int p1,p2;
cin>>p1>>p2;
z[p1][p2]=true;
}
for(int i=1;i<=n;i++)
{
memset(vis,false,sizeof(vis));
if(dfs(i)) ans++;
}
cout<<ans;
return 0;
}
匈牙利算法优化
点击查看代码
//匈牙利算法优化代码
//如需优化,要用编表
//时间复杂度:最坏是O(n*边数)
#include <bits/stdc++.h>
using namespace std;
vector<int> z[maxn];//z[i][j]代表左边第i个点和右边第j个点能不能匹配
bool vis[maxn];//vis[i]代表右边第i个点在这一轮中有没有被请求匹配过
int n,m,k,result[maxn],ans;//n:左边有n个点,m:右边有m个点,k:中间有k条边,result[i]代表右边第i个点和左边第result[i]个点匹配
void add_edge(int s,int e)
{
z[s].push_back(e);
}
bool dfs(int i)//让左边第i个点尝试匹配,返回是否成功
{
for(int k=0;k<z[i].size();k++)//让左边的第i个点和右边第j个点尝试匹配
{
int j=z[i][k];
if(!vis[j])
{
vis[j]=true;
if(!result[j]||dfs(result[j]))
{
result[j]=i;
return true;
}
}
}
return false;
}
signed main()
{
cin>>n>>m>>k;
for(int i=1;i<=k;i++)
{
int p1,p2;
cin>>p1>>p2;
add_edge(p1,p2);
}
for(int i=1;i<=n;i++)
{
memset(vis,false,sizeof(vis));
if(dfs(i)) ans++;
}
cout<<ans;
return 0;
}
tarjan
点击查看代码
//tarjan代码,能找出图中的强连通分量
#include<bits/stdc++.h>
using namespace std;
vector<int> z[maxn];
void add_edge(int s,int e)
{
z[s].push_back(e);
}
int num,n,m;;//当前已经 DFS了num个点
int dfn[maxn];//dfn[i]第i个点是第几个被 DFS到的
int low[maxn];//low[i]代表从i点出发,沿着回边,树边or能扩大环的横叉边走能够走到的所有点中dfn最小的点(深度较小)
stack<int> s;//栈用来储存被DFS过,但还没有求出强连通分量的点
bool instack[maxn];//instack[i]代表i是否在栈里面
int cnt;//有几个强连通分量
int belong[maxn];//belong[i]表示i属于哪一个强连通分量
void dfs(int i)//当前搜索到了i点
{
num++;
dfn[i]=low[i]=num;
s.push(i);
instack[i]=true;
for(int k=0;k<z[i].size();k++)
{
int j=z[i][k];
if(!dfn[j])//这是一条树边
{
dfs(j);
low[i]=min(low[i],low[j]);
}
else//非树边,这是一条回边or能扩大环的横叉边
{
if(instack[j])
{
low[i]=min(low[i],dfn[j]); //low[i]=min(low[i],low[j]); 两种写法都可以
}
}
}
if(dfn[i]==low[i])
{
cnt++;//多了一个强连通分量
while(s.top()!=i)
{
belong[s.top()]=cnt;
instack[s.top()]=false;
s.pop();
}
s.pop();
instack[i]=false;
belong[i]=cnt;
}
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int p1,p2;
cin>>p1>>p2;
add_edge(p1,p2);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])
{
dfs(i);
}
}
return 0;
}
DP斐波那切数列第一种
点击查看代码
//DP斐波那切数列第一种:用别人的值来求自己,需要掌握
#include <bits/stdc++.h>
using namespace std;
int f[100010],n;
int main()
{
cin>>n;
f[0]=0;
f[1]=1;
for(int i=2;i<=n;i++)
{
f[i]=f[i-1]+f[i-2];
}
cout<<f[n]<<endl;
}
DP斐波那切数列第二种
点击查看代码
//DP斐波那切数列第二种:用自己的值去求别人的值,需要掌握
#include <bits/stdc++.h>
using namespace std;
int f[100010],n;
int main()
{
cin>>n;
f[0]=0;
f[1]=1;
for(int i=0;i<=n;i++)
{
f[i+1]+=f[i];
f[i+2]+=f[i];
}
cout<<f[n]<<endl;
}
DP斐波那切数列第三种
点击查看代码
//DP斐波那切数列第三种:记忆化搜索,不需掌握
#include <iostream>
using namespace std;
int n;
bool g[100010];//bool g[i]:斐波那切数列第i项有没有求出来
int f[100010];//int f[i]:斐波那切数列第i项的值
int dfs(int n)//求斐波那切数列第n项的值
{
if(n==0)
{
return 0;
}
if(n==1)
{
return 1;
}
if(g[n])
{
return f[n];
}
g[n]=true;
return f[n];
}
int main()
{
cin>>n;
cout<<dfs(n)<<endl;
return 0;
}
原始排列DP
点击查看代码
//排列DP原始代码
//O(n^4)
#include<bits/stdc++.h>
using namespace std;
int f[maxn][maxn];//f[i][j] i代表插入到哪个数
int n;
int main()
{
cin>>n;
f[1][0]=1;
for(itn i=1;i<n;i++)
{
for(int j=0;j<i*(i-1)/2;j++)//n个数最多形成 n*(n-1)/2 个逆序对
{
for(int k=0;k<=i;k++)
{
//i+1要插入到第k个位置
f[i+1][j+i-k]+=f[i][j];
}
}
}
int ans=0;
for(int i=0;i<=n*(n-1)/2;i+=2)
{
ans+=f[n][i];
}
cout<<ans;
return 0;
}
排列DP优化代码
点击查看代码
//O(n^2)
#include<bits/stdc++.h>
using namespace std;
int f[maxn][maxn];//j=0 偶数个 j=1 奇数个
int n;
int main()
{
cin>>n;
f[1][0]=1;
for(itn i=1;i<n;i++)
{
for(int j=0;j<2;j++)//n个数最多形成 n*(n-1)/2 个逆序对
{
for(int k=0;k<=i;k++)
{
//i+1要插入到第k个位置
f[i+1][(j+i-k)%2]+=f[i][j];
}
}
}
int ans=f[n][0];
cout<<ans;
return 0;
}
求子序列代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
int a[maxn];
int f[maxn];
//最后一个选的数是a[i]的情况下最多能选几个数
int g[maxn];
//g[i]代表 状态f[i] 是由状态 f[g[i]] 转移过来的
void print(int p)
{
//输出当前最后一个数是a[p]的那组解
if(!p) return;
print(g[p]);
cout<<a[p]<<' ';
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
//求f[i]
for(int j=1;j<i;j++)
{
//倒数第2个数是a[j]
if(a[j]<a[i])
{
if(f[j]>f[i])
{
f[i]=f[j];
g[i]=j;
}
}
}
f[i]++;
}
int p=1;
for(int i=2;i<=n;i++)
{
if(f[i]>f[p])
{
p=i;
}
}
cout<<f[p]<<endl;
print(p);
return 0;
}
求最优解方案数答案
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
int a[maxn];
int f[maxn];
//最后一个选的数是a[i]的情况下最多能选几个数
int g[maxn];
//g[i]代表 状态f[i] 是由状态 f[g[i]] 转移过来的
int h[maxn];
//h[i]代表 状态f[i] 有多少种最优解
void print(int p)
{
//输出当前最后一个数是a[p]的那组解
if(!p) return;
print(g[p]);
cout<<a[p]<<' ';
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
h[i]=1;
}
for(int i=1;i<=n;i++)
{
//求f[i]
for(int j=1;j<i;j++)
{
//倒数第2个数是a[j]
if(a[j]<a[i])
{
if(f[j]>f[i])
{
f[i]=f[j];
g[i]=j;
h[i]=h[j];
}
else if(f[j]==f[i])
{
h[i]+=h[j];
//都是最优解
}
}
}
f[i]++;
}
int p=1;
for(int i=2;i<=n;i++)
{
if(f[i]>f[p])
{
p=i;
}
}
cout<<f[p]<<endl;
print(p);
cout<<endl;
int ans=0;
for(int i=1;i<=n;i++)
{
if(f[i]==f[p]) ans++;
}
cout<<ans<<endl;
return 0;
}
01背包DP代码
点击查看代码
//01背包DP代码
//时间复杂度:O(nm)=O(物品数量*体积最大值)
#include <bits/stdc++.h>
using namespace std;
int f[1000][1000],n,m,v[1000],w[1000];//f[i][j]代表前i个物品已经考虑完,用掉了j的体积所能获得的最大价值
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v[i]>>w[i];//读入体积和价值
}
for(int i=0;i<n;i++)
{
for(int j=0;j<=m;j++)//考虑第i个物品选不选
{
f[i+1][j]=max(f[i+1][j],f[i][j]);//第i+1个物品不选
f[i+1][j+v[i+1]]=max(f[i+1][j+v[i+1]],f[i][j]+w[i+1]);//第i+1个物品选
}
}
int ans=0;
for(int i=0;i<=m;i++)
{
ans=max(ans,f[n][i]);
}
cout<<ans<<endl;//cout<<f[n][m]<<endl;
return 0;
}
无穷背包DP代码原始版本
点击查看代码
//无穷背包问题原始版本代码
//时间复杂度:O(nm^2)
#include <bits/stdc++.h>
using namespace std;
int f[1000][1000],n,m,v[1000],w[1000];//f[i][j]代表前i个物品已经考虑完,用掉了j的体积所能获得的最大价值
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v[i]>>w[i];//读入体积和价值
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)//要求状态f[i][j]用别人来求自己的值
{
for(int k=0;k*v[i]<=j;k++)//第i个物品选k个
{
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
}
}
}
int ans=0;
for(int i=0;i<=m;i++)
{
ans=max(ans,f[n][i]);
}
cout<<ans<<endl;//cout<<f[n][m]<<endl;
return 0;
}
无穷背包DP代码优化
点击查看代码
//无穷背包问题优化代码
//时间复杂度:O(nm)
#include <bits/stdc++.h>
using namespace std;
int f[1000][1000],n,m,v[1000],w[1000];//f[i][j]代表前i个物品已经考虑完,用掉了j的体积所能获得的最大价值
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v[i]>>w[i];//读入体积和价值
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)//要求状态f[i][j]用别人来求自己的值
{
f[i][j]=f[i-1][j];//上
if(j>=v[i])
{
f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);//左
}
}
}
int ans=0;
for(int i=0;i<=m;i++)
{
ans=max(ans,f[n][i]);
}
cout<<ans<<endl;//cout<<f[n][m]<<endl;
return 0;
}
判断二分图
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=100010;
//vector存图:边表
//缺点:取i->j这条边的信息只需要O(n)
//优点:只需要O(n+m)的内存
vector<int> g[maxn];//g[i] 用来存储所有从i出发的边
void add_edge(int s,int e)//添加一条从s -> e
{
g[s].push_back(e);
}
int col[maxn],n,m;
//col[i]==0 i点还没决定放哪边
//col[i]==1 i点放左边
//col[i]==2 i点放右边
signed main()
{
cin >>n >> m;
for (int i=1;i<=m;i++)
{
int s,e;
cin >> s >> e;
add_edge(s,e);
add_edge(e,s);
}
bool able=true;
for (int i=1;i<=n;i++)
{
if (col[i] == 0)
{
col[i] = 1;
queue<int> q;//还需要更新周围点放哪边的那些点
q.push(i);
while (q.size())
{
int now=q.front();
q.pop();
for (int i=0;i<g[now].size();i++)
{
int j=g[now][i];//是一条从now -> j的边
if (col[j] == 0) //j点还没有放
{
col[j] = 3-col[now];
q.push(j);
}
else if (col[now] == col[j])
{
able=false;
}
}
}
}
}
if (able)
{
cout << "yes\n";
}
else
{
cout << "no\n";
}
return 0;
}
KMP代码
点击查看代码
#include <bits/stdc++.h>
using namespace std;
// 求 字符串 s 的 next 数组
void calc_next(string s, int nxt[])
{
int n = s.length();
nxt[0] = -1;
nxt[1] = 0;
for (int k = 1; k < n; ++k)
{
// 计算 nxt[k+1]
int p = nxt[k];
while (p)
{
if (s[p] == s[k])
{
break;
}
else
{
p = nxt[p];
}
}
nxt[k+1] = p + (s[p] == s[k]);
// printf("%d %d\n", k+1, nxt[k+1]);
}
}
int nxt_t_concat_s[2000010],nxt_t[1000010];
signed main()
{
string s, t;
cin >> s >> t;
calc_next(t + "#" + s, nxt_t_concat_s);
int m = t.length(), n = s.length();
for (int i = m+2; i <= n+m+1; ++i)
{
if (nxt_t_concat_s[i] == m)
printf("%d\n", i-m-m);
}
calc_next(t, nxt_t);
for (int i = 1; i <= t.length(); ++i)
printf("%d ", nxt_t[i]);
printf("\n");
return 0;
}

浙公网安备 33010602011771号