第二章 练习 Part II
POJ 1635 Subway tree system
这个题有点意思,因为每条边只走两次,所以当出现1时出说明有一个分支已经走完了 比如01 说明树有一个只有一个结点的子树。
我的一个想法就是比较两个字符串中含有连续0的种类和个数是否一样(因为输入是correct exploration)写出了代码 运行起来发现示例没过,仔细观察了一下 发现自己太NAIVE了
示例的前两个 第二有连续的3个零 而第一没有 但是他们是same的
修改一下 每回碰到1的时候不重新计数,而是把cnt减一 这样cnt就表示层数了,改完以后的代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<utility>
#include<climits>
#include<cstring>
#include<cassert>
using namespace std;
const int N = 3004;
char s[N],t[N];
int cnts[N],cntt[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
memset(cnts,0,sizeof(cnts));
memset(cntt,0,sizeof(cntt));
scanf("%s",s+1);
scanf("%s",t+1);
int cnt = 0,tots = 0,tott = 0,flag = 1,flip = 0;//flag为0时说明发现了不等 那么结果就为false
for(int k=1;s[k];k++){
if(s[k] == '0')
cnt++,tots++,flip = 1;
else{
cnts[cnt]++;
cnt--;
flip = 0;//flip是控制当连续个1出现时 第二次出现1开始不更新cnt直到0出现为止
}
}
cnt = 0;
for(int k=1;t[k];k++){
if(t[k] == '0')
cnt++,tott++,flip = 1;
else{
cntt[cnt]++;
cnt--;
flip = 0;
}
}
for(int w=1;tots && tott;w++){
if(cnts[w] == cntt[w])
tots -=cnts [w],tott -= cntt[w];
else{
flag = 0;
break;
}
}
if(flag && !tots && !tott) puts("same");
else puts("different");
}
return 0;
}
测试数据就一个 好难debug啊。。
我想到比较两个树的所有叶子结点的深度是否相等来判断 后来发现反例
0 - 1 0 - 2 1 - 3 1 - 4 1 - 5 2 - 6
0 - 1 0 - 2 1 - 3 1 - 4 2 - 5 2 - 6
两者都是7个结点 且所有叶子结点 4个深度为2(层数为3)
光盘里面的方法是 最小表示法 康一康:
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
string s1, s2;
string w(string s) {
vector<string> a;
string f = "";
int num = 0, t = 0;
for (unsigned int i = 0; i < s.size(); i++) {
if (s[i] == '0') num++;
else num--;
if (!num) {
if (i - 1 > t + 1) a.push_back("0" + w(s.substr(t + 1, i - 1 - t)) + "1");
else a.push_back("01");
t = i + 1;
}
}
sort(a.begin(), a.end());
for (unsigned int i = 0; i < a.size(); i++) f += a[i];
return f;
}
int main() {
int t;
cin >> t;
while (t--) {
cin >> s1 >> s2;
cout << (w(s1) == w(s2) ? "same" : "different") << endl;
}
return 0;
}
w就是把一棵树变成最小表示,w函数里面,遍历所有字符,通过num来找到当前结点的一棵子树,num为0时 s [ t , i ] s[t,i] s[t,i]就是一棵子树 然后递归地处理 s [ t , i ] s[t,i] s[t,i] ,得到 s [ t , i ] s[t,i] s[t,i]的最小表示,遍历完所有字符也就得到了所有子树的最小表示,然后进行排序之后 拼接起来 就是以当前结点为根的数的最小表示!
POJ 2185 Milking Grid
这题看的头疼,
他要的rectangular unit of smallest area,看这个输出感觉他要的是一维的rectangular unit
如果碰到
ABC
DEF
的话,仅仅是一维是不够的 又看到一个条件:dimensions of the small rectangular unit do not necessarily need to divide evenly the dimensions of the entire milking grid,说明ABCDEF就能满足要求
我理解错了,题目要求的输出是area of the smallest unit,输出的是面积 所以不用考虑一维不一维的
这题看了yxc的解析 没看懂 有点郁闷!!
首先想一维的情况,next[n]表示最大整数满足前缀1…next[n] 与 后缀n-next[n]+1 … n相等,那么对于一维的情况 我猜想最小面积的覆盖单元是子串1 … n-next[n]
举例:
abcd abcd abcd abc ,next[n]为11 最小覆盖单元为abcd 即1…(15-11)
acbd efghijklmn abcd next[n]为4 最小覆盖单元为abcd efghijklmn 即1… (18-4)
证明一下:首先证明1… n-next[n]是可以覆盖整个字符串的,因为剩下部分n-next[n]+1…n可以由1…next[n]覆盖所以可以覆盖的,然后证明没有比这个短的前缀字符串可以覆盖整个字符串:
假设存在一个子字符串[1,3,2]能覆盖整个字符串,1,2,3分别代表字符串,假设这个串只循环了一次,整个串可以表示为:(2可以理解成[1,3,2]用来覆盖首部,1可以理解成[1,3,2]用来覆盖尾部)
[2,1,3,2,1] next[n]的长度应为串2和串1的和,n-next[n]就刚好为[1,3,2]的长度
假设这个串循环了k次即:
[2,1,3,2,1,3,2,…,1,3,2,1] next[n]就为2,1,3,2,…,3,2,1 即2的长度加上k-1个1,3,2再加上一个1,n-next[n]就刚好为[1,3,2]的长度 综上以上情况可知对于1n的字符串 n-next[n]为最小覆盖该字符串的子字符串的长度
对于2n的字符矩阵,第一行的next[n]为a,第二行的next[n]为b,单单覆盖第一行 只需要n-a的长度的字符串,单单覆盖第二行 只需要n-b长度的字符串
我的证明不够严谨,没看懂的话,移步到下面这个博客
https://blog.csdn.net/fjsd155/article/details/6866991
对于证明n-next[n]是可以覆盖整个字符串的 我只证明了 next[n] < n - next[n]的情况,当next[n] >= n - next[n]的时候 证明见博客
光盘里面的代码:
//Author:XuHt
#include <cstdio>
#include <cstring>
#include <iostream>
#define ull unsigned long long
using namespace std;
const int R = 10006, C = 81, P = 13331;
int r, c, nxt[R];
char s[R][C];
ull H[R];
int work(int len) {
int i = 0, j = -1;
nxt[0] = -1;
while (i < len)
if (j == -1 || H[i] == H[j]) nxt[++i] = ++j;
else j = nxt[j];
return len - nxt[len];
}
int main() {
cin >> r >> c;
for (int i = 0; i < r; i++) scanf("%s", s[i]);
memset(H, 0, sizeof(H));
for (int i = 0; i < r; i++)
for (int j = 0; j < c; j++)
H[i] = H[i] * P + s[i][j];
int ans = work(r);
memset(H, 0, sizeof(H));
for (int i = 0; i < c; i++)
for (int j = 0; j < r; j++)
H[i] = H[i] * P + s[j][i];
ans *= work(c);
cout << ans << endl;
return 0;
}
思路是计算每行的字符串hash值 然后把整个行当做一个字符,然后按垂直方向进行KMP算法,得到高
接着,计算每列的字符串的hash值 然后把整个列当做一个字符,然后按水平方向进行KMP算法,得到宽 结果是高乘上宽