E. Compress Words
题意:
A
m
u
g
a
e
Amugae
Amugae有一个由n个单词组成的句子。他想把这个句子压缩成一个词。
A
m
u
g
a
e
Amugae
Amugae不喜欢重复,所以当他将两个单词合并成一个单词时,他删除第二个单词中与第一个单词后缀重合的最长前缀。例如,他将
s
a
m
p
l
e
sample
sample和
p
l
e
a
s
e
please
please合并成
s
a
m
p
l
e
a
s
e
samplease
samplease。
A
m
u
g
a
e
Amugae
Amugae将从左到右合并他的句子(即首先合并前两个单词,然后合并结果与第三个单词,以此类推)。编写一个程序,在合并过程结束后打印压缩后的单词。
题解:求
a
a
a的后缀与
b
b
b的前缀重合的最大长度 (
e
x
k
m
p
exkmp
exkmp模板题)
光顾着套板子,自己都不动脑子了,由于
e
x
k
m
p
exkmp
exkmp的
e
x
[
i
]
ex[i]
ex[i]表示
s
s
s以
i
i
i开头的后缀与
t
t
t 的前缀相同的长度,
i
i
i的取值范围为
[
1
,
l
e
n
]
[1,len]
[1,len],直接套肯定超时,毕竟求了很多没有用的东西。所以只需要考虑
[
l
e
n
−
l
2
,
l
e
n
]
[len-l2,len]
[len−l2,len]区间内的公共前缀就好了,在板子的基础上改一点。
#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
#define next nx
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
const int N=1e6+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,cnt;
string s[N];
char ans[N],tmp[N];
int next[N],ex[N]; //ex数组即为extend数组
//预处理计算next数组
void GETNEXT(char *str,int len)
{
int i=0,j,po;
next[0]=len;//初始化next[0]
while(str[i]==str[i+1]&&i+1<len)//计算next[1]
i++;
next[1]=i;
po=1;//初始化po的位置
for(i=2;i<len;i++)
{
if(next[i-po]+i<next[po]+po)//第一种情况,可以直接得到next[i]的值
next[i]=next[i-po];
else//第二种情况,要继续匹配才能得到next[i]的值
{
j=next[po]+po-i;
if(j<0)j=0;//如果i>po+next[po],则要从头开始匹配
while(i+j<len&&str[j]==str[j+i])//计算next[i]
j++;
next[i]=j;
po=i;//更新po的位置
}
}
}
//计算extend数组
void EXKMP(char *s1,char *s2,int len,int l2)
{
/*************修改ex数组的起始点*****************/
int i=max(len-l2,0),j=0,po;
GETNEXT(s2,l2);//计算子串的next数组
while(s1[i]==s2[j]&&j<l2&&i<len)//计算ex[0]
i++,j++;
ex[max(len-l2,0)]=j;
po=max(len-l2,0);//初始化po的位置
/*******************************************/
for(i=max(len-l2,0)+1;i<len;i++)
{
if(next[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值
ex[i]=next[i-po];
else//第二种情况,要继续匹配才能得到ex[i]的值
{
j=ex[po]+po-i;
if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配
while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]
j++;
ex[i]=j;
po=i;//更新po的位置
}
}
}
//scanf("%lld%lld",&n,&m);
//printf("%lld\n",ans);
//for(int i=1;i<=n;i++)
int main()
{
int n; cin>>n;
for(int i=0;i<n;i++) cin>>s[i];
for(int i=0;i<(int)s[0].size();i++)
ans[cnt++]=s[0][i]; ans[cnt]='\0';
for(int i=1;i<n;i++){
m=0;
for(int j=0;j<(int)s[i].size();j++) tmp[m++]=s[i][j];
tmp[m]='\0';
EXKMP(ans,tmp,cnt,m);
int st=0;
for(int j=max(0,cnt-m);j<cnt;j++){
if(j+ex[j]==cnt){
st=ex[j];
break;
}
}
for(int j=st;j<m;j++) ans[cnt++]=tmp[j];
}
for(int i=0;i<cnt;i++) cout<<ans[i];
}
D. White Lines
题意:
选择覆盖
k
∗
k
k*k
k∗k的区域使得这个区域全为白色,使得一行或一列全为白色的数目最多,输出最多的数目。
题解:(二维滑动窗口)
如果使得一行全为白色有两种情况:
第一种:本身即为白色行
第二钟:一行中最左面的黑色点和最右面的黑色点都在
k
∗
k
k*k
k∗k的区域内
#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
const int N=5e5+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,z,k,cnt,t,len,q;
char maps[2005][2005];
int ans[2005][2005],l[N],r[N],u[N],d[N];
//scanf("%lld%lld",&n,&m);
//printf("%lld\n",ans);
//for(int i=1;i<=n;i++)
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%s",maps[i]+1);
for(int j=1;j<=n;j++)
if(maps[i][j]=='B')
{
if(l[i]==0) l[i]=j;
if(u[j]==0) u[j]=i;
r[i]=j;
d[j]=i;
}
}
int base_cnt=0;
for(int i=1;i<=n;i++)
{
if(l[i]==0)
base_cnt++;
if(u[i]==0)
base_cnt++;
}
for(int i=1;i<=n;i++)///二维滑动窗口
{
int temp_cnt=0;
for(int j=1;j<=n;j++)
{
if(i>=d[j]&&i-k+1<=u[j]&&u[j]!=0)///可以被覆盖
temp_cnt++;
if(j>k&&i>=d[j-k]&&i-k+1<=u[j-k]&&u[j-k]!=0)///滑动
temp_cnt--;
ans[i][j]+=temp_cnt;
}
}
for(int j=1;j<=n;j++)
{
int temp_cnt=0;
for(int i=1;i<=n;i++)
{
if(j>=r[i]&&j-k+1<=l[i]&&l[i]!=0)
temp_cnt++;
if(i>k&&j>=r[i-k]&&j-k+1<=l[i-k]&&l[i-k]!=0)
temp_cnt--;
ans[i][j]+=temp_cnt;
}
}
int ppp=0;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
ppp = max(ppp,ans[i][j]);
//if(ppp==2) printf("%d %d ",i,j);
}
}
printf("%d",ppp+base_cnt);
}
C. Powers Of Two
题解:会使容器就好办了
模拟一下数的分解操作,例如:8就可以分解为8个二进制数。
#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
const int N=5e5+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,z,k,cnt,t,len,q;
int a[N],b[N],c[N];
//scanf("%lld%lld",&n,&m);
//printf("%lld\n",ans);
//for(int i=1;i<=n;i++)
int main()
{
scanf("%d%d",&n,&k);
if(m>n) return printf("NO\n"),0;
priority_queue<int>Q;
for(int i=0;i<32;i++)
{
if(((1<<i)&n)>0)
Q.push(i);
}
if(Q.size()>k) return printf("NO\n"),0;
printf("YES\n");
while(Q.top()>0&&Q.size()<k)
{
int lim=Q.top();
Q.pop();
Q.push(lim-1);
Q.push(lim-1);
}
while(Q.size()>0)
{
cout<<(1<<Q.top())<<" ";
Q.pop();
}
}
D. Circular Dance
题意:
有n个人连接一个单向环,给出第 i 个人的下一个人和下下个人的编号(有可能是反的),求出这n个人构成的环。
题解:
由于给出的下一人和下下一人的标号,所以可知这两人挨着,所以可以知道任意一个人挨着的两个人,所以可以以任意一个人开头,然后找下去谁和他相互挨着。
注意:就是环找出来了但不一定是顺时针,所以判断一下顺序!!!
#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
const int N=5e5+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,z,k,cnt,t,len,q;
vector<int>a[N];
int ans[N];
//scanf("%lld%lld",&n,&m);
//printf("%lld\n",ans);
//for(int i=1;i<=n;i++)
int main()
{
scanf("%d",&n);
int x1,y1;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
if(i==1) x1=x,y1=y;
a[x].push_back(y);
a[y].push_back(x);
}
ans[0]=a[1][0];
ans[1]=1;
ans[2]=a[1][1];
for(int i=3;i<n;i++)
{
if(a[ans[i-1]][0]==ans[i-2])
ans[i]=a[ans[i-1]][1];
else
ans[i]=a[ans[i-1]][0];
}
if(ans[2]==x1||ans[2]==y1)//判断是否为顺时针
for(int i=0;i<n;i++)
printf("%d ",ans[i]);
else
for(int i=n-1;i>=0;i--)
printf("%d ",ans[i]);
}
E. Almost Regular Bracket Sequence
题意:
给你一个长度为n的括号串,你可以独立的选择一个位置,改变那个位置的括号之后,如果总括号串是平衡的,这就是一个合法位置,求一共有多少个合法的位置。
题解:思维题
正常来说,区间里的 ( 的数目要大于等于 ) ,结束时 ( 等于 ) ,才可以构成合法括号。
根据题意可以发现,修改 ( 和修改 ) 的情况是对称的,所以只需要考虑修改一类括号然后另一种情况,逆着推就好了。
所以可以考虑修改),当应该修改)时,由题意可知)数目一定比(数目多两个,举个例子 :(()))),容易发现规律:满足
a
n
s
[
i
]
ans[i]
ans[i]>-2的 )都可以翻转过来,总可以找到)构成合法括号,因为 i 前面的括号都一定是匹配的。
再考虑:))(()) ,只能翻转第一个),他的
a
n
s
[
i
]
ans[i]
ans[i]=-1,很显然不能翻转第二个
a
n
s
[
i
]
ans[i]
ans[i]=-1的),因为它前面的括号不是全部匹配的。所以可以知道第一个符合
a
n
s
[
i
]
ans[i]
ans[i]为-1的)一定为最后一个不匹配括号,因为如果这个)后还有不匹配的组合,一定不可能通过一步修改完成。所以
a
n
s
[
i
]
ans[i]
ans[i]=-1为循环的终止条件。说的不是很清楚,详见代码。
#include<bits/stdc++.h>
#define mp make_pair
#define maxn 10000000
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
const int N=1e6+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,x,y,z,k,cnt,t,len,q;
int ans[N],ppp[N];///ans 记录右括号 ppp 记录左括号
char a[N];
//scanf("%lld%lld",&n,&m);
//printf("%lld\n",ans);
//for(int i=1;i<=n;i++)
int main()
{
scanf("%d",&n);
scanf("%s",a);
int flag=0;
for(int i=0;i<n;i++)
{
if(a[i]=='(') cnt++;
else cnt--;
ans[i]=cnt;
if(ans[i]<-2) flag=1;
}
for(int i=n-1;i>=0;i--)
{
if(a[i]==')') q++;
else q--;
ppp[i]=q;
if(ppp[i]<-2) flag=1;
}
int len=0;
if(cnt!=2&&cnt!=-2||flag==1)
return printf("0\n"),0;
else if(cnt==2)
{
for(int i=n-1;i>=0;i--)
{
if(a[i]=='('&&ppp[i]>-2)
len++;
if(ppp[i]==-1) break;
}
}
else if(cnt==-2)
{
for(int i=0;i<n;i++)
{
if(a[i]==')'&&ans[i]>-2)
len++;
if(ans[i]==-1) break;
}
}
printf("%d\n",len);
}