A. 智乃的Hello XXXX
随便输出Hello xxx即可。
B. 智乃买瓜
链接:https://ac.nowcoder.com/acm/contest/23478/B
来源:牛客网
题目描述
有一人前来买瓜。
“哥们儿,这瓜多少钱一斤呐”
“两块钱一斤”
“What's up,这瓜皮是金子做的,还是瓜粒子是金子做的”
智乃来到水果摊前买瓜,水果摊上贩卖着NN个不同的西瓜,第ii个西瓜的重量为wiwi。智乃对于每个瓜都可以选择买一个整瓜或者把瓜劈开买半个瓜,半个瓜的重量为wi22wi。
也就是说对于每个西瓜,智乃都有三种不同的决策:
- 购买一整个重量为wiwi的西瓜
- 把瓜劈开,购买半个重量为wi22wi的西瓜
- 不进行购买操作
为了简化题目,我们保证所有瓜的重量都是一个正偶数。
现在智乃想要知道,如果他想要购买西瓜的重量和分别为k=1,2,3...Mk=1,2,3...M时,有多少种购买西瓜的方案,因为这些数字可能会很大,请输出方案数对109+7109+7取余数后的结果。
输入描述:
第一行输入两个整数N,M(0≤N≤103,1≤M≤103)N,M(0≤N≤103,1≤M≤103),分别表示西瓜的数目NN,以及查询的重量上限为MM。
若NN不为00,接下来一行NN个正偶数wi(2≤wi≤2×103)wi(2≤wi≤2×103)表示每个西瓜的重量。
输出描述:
输出一行MM个数字,分别表示购买西瓜的重量和为k=1,2,3...Mk=1,2,3...M时,有多少种购买西瓜的方案,因为这些数字可能会很大,请输出方案数对109+7109+7取余数后的结果。
示例1
输入
复制
3 6
8 2 4
输出
复制
1 2 1 3 2 3
比较裸的背包,dp[i, j]表示用前i个西瓜凑出j这个重量的方案数。需要注意的是dp数组第二维要开2e3,虽然m不超过1e3,但在代码中dp[i, w[i]] += 1;这一句涉及了wi,而wi可能达到2e3。转移方程见代码:
#include <iostream>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#define int long long
#define mod 1000000007
#define ll long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
ll dp[1005][2005] = { 0 };
ll w[2005];
ll n, m;
signed main() {
cin >> n >> m;
for(ll i = 1; i <= n; i++) {
cin >> w[i];
}
for(ll i = 1; i <= n; i++) {
dp[i][w[i]] += 1;
dp[i][w[i] / 2] += 1;
for(int j = 1; j <= m; j++) {
dp[i][j] = (dp[i - 1][j] + dp[i][j]) % mod;
if(j - w[i] >= 0) dp[i][j] = (dp[i - 1][j - w[i]] + dp[i][j]) % mod;
if(j - w[i] / 2 >= 0) dp[i][j] = (dp[i - 1][j - w[i] / 2] + dp[i][j]) % mod;
}
}
for(int i = 1; i <= m; i++) {
cout << dp[n][i] << " ";
}
return 0;
}
C. 智乃买瓜(another version)
链接:https://ac.nowcoder.com/acm/contest/23478/C
来源:牛客网
题目描述
本题是原*版*的另一个版本,简单来讲,本题是原版输入输出的倒置。
有一人前来买瓜。
“哥们儿,这瓜多少钱一斤呐”
“两块钱一斤”
“What's up,这瓜皮是金子做的,还是瓜粒子是金子做的”
智乃来到水果摊前买瓜,水果摊上贩卖着若干个不同的西瓜,第ii个西瓜的重量为wiwi。智乃对于每个瓜都可以选择买一个整瓜或者把瓜劈开买半个瓜,半个瓜的重量为wi22wi。
也就是说对于每个西瓜,智乃都有三种不同的决策:
- 购买一整个重量为wiwi的西瓜
- 把瓜劈开,购买半个重量为wi22wi的西瓜
- 不进行购买操作
为了简化题目,我们保证所有瓜的重量都是一个正偶数。
现在智乃知道,购买西瓜的重量和分别为k=1,2,3...Mk=1,2,3...M时,购买西瓜的方案的种类数对109+7109+7取余数后的结果。
她想要还原水果摊贩卖的这若干个不同的西瓜重量分别为多少,请你构造一个贩卖NN个不同西瓜的水果摊,其购买西瓜的重量和满足智乃的要求,当出现有多种符合题目描述的答案时,你只用输出任意一种。
我们保证输入的数据至少存在一个N≤103N≤103的合法解。
输入描述:
第一行输入一个正整数M(1≤M≤103)M(1≤M≤103),表示智乃所知购买西瓜质量和的上限。
接下来一行MM个整数,第ii个整数表示购买西瓜的重量和为ii时,购买西瓜的方案的种类数对109+7109+7取余数后的结果。
输出描述:
首先输出一个整数NN,西瓜的数目。要求你给出的NN大小范围在[0,103][0,103]之间
接下来一行NN个正偶数wiwi,表示每个每个西瓜的重量。要求你给出的wiwi大小范围在[2,2×103][2,2×103]之间,且wiwi为偶数。
输入数据保证,至少存在一组在输出范围内的合法解。
示例1
输入
复制
6
1 2 1 3 2 3
输出
复制
3
8 2 4
比赛时没有出思路,dp还是要多练~~~
首先容易知道2的个数是确定的。不放倒着考虑,在B题中dp[i, j]的方案数是怎么得到的,这里就怎么减去。思路就是在把一个个dp方案数变为0的过程中不断将使用的重量放到答案vector中。从前往后遍历dp数组,如果一个位置的值不为0,说明这些剩下的这个重量i对应的方案数只能由i*2这个西瓜/2来提供。此时就把一个i*2放入ans,然后更新后面的dp数组部分。
#include <iostream>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#define mod 1000000007
#define int long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
int m, dp[1005], w[1005];
int c[2005] = { 0 };
int cnt = 0;
signed main() {
cin >> m;
for(int i = 1; i <= m; i++) {
cin >> dp[i];
}
vector<int> ans;
dp[0] = 1;//不要忘记
for(int i = 1; i <= m; i++) {
while(dp[i]) {
ans.push_back(i * 2);
for(int j = i; j <= m; j++) {//i==j时就是处理dp[i],剩下的方案数由dp[i]个i*2的瓜提供,因此dp[0]要设置为1,即每次都要减1
dp[j] = (dp[j] - dp[j - i] + mod) % mod;
if(j - i * 2 >= 0) dp[j] = (dp[j] - dp[j - i * 2] + mod) % mod;
}
}
}
cout << ans.size() << endl;
for(auto x : ans) {
cout << x << " ";
}
return 0;
}
D. 智乃的01串打乱
纯签,随便选两个01位置交换即可。
E. 智乃的数字积木
链接:https://ac.nowcoder.com/acm/contest/23478/E
来源:牛客网
题目描述
本题有对应的hard version,两者的区别仅在数据范围,保证easy version的测试用例集是hard version的子集。
智乃酱有NN块积木,每一块积木都有自己的颜色以及数字,这NN块积木颜色的范围从11到MM,数字的范围从00到99。
现在智乃酱把这些积木从左到右排成一排,这样积木看上去就形成了一个大整数,智乃觉得这个数字不够大,所以他决定交换一些积木使得这个大整数尽可能的大。
具体来讲,智乃可以在任意时刻无限次的交换相邻且同色的数字积木。
但是即使这样,智乃觉得这个数字还是不够大。
所以智乃酱拿出了她的油漆桶,她决定进行KK次操作,每次操作都选中两种颜色PP,QQ,然后将所有颜色为PP的积木染成颜色QQ。
当然,在染色结束后智乃酱也是可以交换相邻同色积木进行调整。
现在智乃想要知道,她进行KK次染色操作之前,以及每次染色后能够通过交换得到最大的正整数是多少,当然啦,这个大整数被允许包含前导零。
因为这个大整数很大,所以只要求你输出答案对109+7109+7取余数的结果即可。
输入描述:
第一行输入三个整数N,M,K(1≤N,M≤105,0≤K≤10)N,M,K(1≤N,M≤105,0≤K≤10)分别表示积木的数目,初始颜色的种类,以及智乃酱染色的次数。
接下来输入一个长度为NN的字符串,字符串仅由数字0−90−9组成。
接下来一行NN个正整数coli(1≤coli≤M)coli(1≤coli≤M)表示积木的颜色。
接下来KK行,每行输入两个正整数P,Q(1≤P,Q≤M)P,Q(1≤P,Q≤M)表示选中两种颜色PP,QQ,然后将所有颜色为PP的积木染成颜色QQ。
输出描述:
输出K+1K+1行,即她进行KK次染色操作之前,以及每次染色后能够通过交换得到最大的正整数对109+7109+7取余数的结果。
示例1
输入
复制
10 2 0
9586547120
1 2 1 2 1 1 1 1 1 1
输出
复制
586754147
示例2
输入
复制
10 2 1
9586547120
1 2 1 2 1 1 1 1 1 1
1 2
输出
复制
586754147
876554147
示例3
输入
复制
5 5 4
12345
1 2 3 4 5
3 2
2 5
4 5
5 1
输出
复制
12345
13245
13245
15432
54321
注意到k范围很小,因此每次染色直接暴力处理,然后对于同色的区间暴力排序,再暴力遍历计算字符串的值即可。
#include <iostream>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#define mod 1000000007
#define ll long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
int n, m, k;
char s[100005];
int c[100005];
bool cmp(char a, char b) {
return a > b;
}
int main() {
cin >> n >> m >> k;
c[n + 1] = -1;
scanf("%s", s + 1);
for(int i = 1; i <= n; i++) {
cin >> c[i];
}
int lst = 1;
for(int i = 2; i <= n + 1; i++) {
if(c[i] != c[i - 1]) {
sort(s + lst, s + (i - 1) + 1, cmp);
lst = i;
}
}
ll ans = 0;
for(int i = 1; i <= n; i++) {
ans = (ans * 10) % mod;
ans = (ans + s[i] - '0') % mod;
}
cout << ans << endl;
for(int i = 1; i <= k; i++) {
int p, q;
cin >> p >> q;
for(int j = 1; j <= n; j++) {
if(c[j] == p) c[j] = q;
}
int lst = 1;
for(int j = 2; j <= n + 1; j++) {
if(c[j] != c[j - 1]) {
sort(s + lst, s + (j - 1) + 1, cmp);
lst = j;
}
}
ans = 0;
for(int i = 1; i <= n; i++) {
ans = (ans * 10) % mod;
ans = (ans + s[i] - '0') % mod;
}
cout << ans << endl;
}
return 0;
}
G. 智乃的树旋转
链接:https://ac.nowcoder.com/acm/contest/23478/G
来源:牛客网
题目描述
本题有对应的hard version, *easy version有额外的限制条件*,保证easy version的测试用例集是hard version的子集。
树旋转是在二叉树中的一种子树调整操作, 每一次旋转并不影响对该二叉树进行中序遍历的结果。
树旋转通常应用于需要调整树的局部平衡性的场合。树旋转包括两个不同的方式,分别是左旋和右旋。 两种旋转呈镜像,而且互为逆操作。
如图所示,为树的左旋转和右旋转示意图。当AA节点位于BB节点的左子树时,可以进行以AA节点为旋转轴的右旋转,反之可以进行以BB节点为旋转轴的左旋转。
具体来讲
右旋转:
当节点AA位于节点BB的左孩子时,节点AA可作为旋转轴进行右旋转,该旋转操作涉及到结构改变的节点有44个。
我们记旋转前AA节点的右孩子为ββ节点,BB节点的父节点为XX节点(若BB节点就是整棵树的根节点,则无需做与XX节点相关的操作)。
则旋转后ββ节点由AA节点的右孩子变为BB节点的左孩子,XX节点的其中一个孩子节点由BB节点改变为AA节点,并且它们和XX节点的中序顺序不发生其他变化;也就是说原来BB节点是XX节点的右孩子,则旋转后变为AA节点替换BB节点成为XX节点新的右孩子,反之若BB节点初始是XX节点的左孩子,则旋转后AA节点替换BB节点成为XX节点新的左孩子。
左旋转:
当节点BB位于节点AA的右孩子时,节点BB可作为旋转轴进行左旋转,该旋转操作涉及到结构改变的节点有44个。
我们记旋转前BB节点的左孩子为ββ节点,AA节点的父节点为XX节点(若AA节点就是整棵树的根节点,则无需做与XX节点相关的操作)。
则旋转后ββ节点由BB节点的左孩子变为AA节点的右孩子,XX节点的其中一个孩子节点由AA节点改变为BB节点,并且它们和XX节点的中序顺序不发生其他变化;也就是说原来AA节点是XX节点的右孩子,则旋转后变为BB节点替换AA节点成为XX节点新的右孩子,反之若AA节点初始是XX节点的左孩子,则旋转后BB节点替换AA节点成为XX节点新的左孩子。
智乃最近学习了树旋转,树旋转的本质是二叉树旋转轴节点与其父节点父子关系的改变,从视觉效果上看起来好像整个树进行了“旋转”。
在实际的操作过程中,如果指定旋转轴,其实就没有所谓“左旋转”和“右旋转”的区别,当旋转轴确定时,将只有一种旋转方法,遵循以下规则。
- 根节点无法作为旋转轴被选定。
- 当旋转轴是其父节点的左子树时,旋转轴只能进行右旋转。
- 当旋转轴是其父节点的右子树时,旋转轴只能进行左旋转。
现在智乃有一颗尺寸大小为NN二叉树,智乃对其做了一次旋转操作将其打乱,她想让你通过一系列树的旋转操作将其还原,要求你的操作次数不多于N2N2次。
输入描述:
第一行输入一个正整数N(1≤N≤103)N(1≤N≤103),表示二叉树的节点数目,节点从11到NN标号。
接下来NN行输入二叉树一开始的样子。
每行输入两个非负整数lch,rch(0≤lch,rch≤N)lch,rch(0≤lch,rch≤N)表示每个节点的左右子树。
当lchlch的值为00时,则表示该节点无左子树,当rchrch的值为00时,则表示该节点无右子树。
接下来NN行输入二叉树被打乱后的样子。
每行输入两个非负整数lch,rch(0≤lch,rch≤N)lch,rch(0≤lch,rch≤N)表示每个节点的左右子树。
当lchlch的值为00时,则表示该节点无左子树,当rchrch的值为00时,则表示该节点无右子树。
要求你将打乱后的二叉树通过一系列旋转操作还原
输出描述:
首先输出一个整数KK,表示你还原二叉树进行旋转的次数,要求你给出KK的范围在[0,N2][0,N2],接下来KK行,依次输出旋转操作的旋转轴。
由于旋转轴只能进行左旋转或者右旋转其中的一种,裁判程序将会自己判断当前需要进行的是左旋转还是右旋转。
注意:旋转过程中的根节点无法作为旋转轴进行旋转,如果你指定旋转过程中的根节点作为旋转轴,则裁判程序将直接给出WA的结果。
easy version额外限制条件,保证可以通过不多于一次树旋转的操作还原二叉树,即一定存在K≤1K≤1的解。
示例1
输入
复制
5
2 3
0 0
4 5
0 0
0 0
2 4
0 0
1 5
0 0
0 0
输出
复制
1
1
easy version比较简单,因为最多只旋转一次,因此旋转的只能是前后两棵树之间父子关系互换的两个点。
#include <iostream>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#define ll long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
int n;
struct node {
int ls, rs;
} n1[1005], n2[1005];
int fa1[1005], fa2[1005];
int main() {
cin >> n;
for(int i = 1; i <= n; i++) {
int l, r;
cin >> l >> r;
n1[i].ls = l, n1[i].rs = r;
fa1[l] = fa1[r] = i;
}
for(int i = 1; i <= n; i++) {
int l, r;
cin >> l >> r;
n2[i].ls = l, n2[i].rs = r;
fa2[l] = fa2[r] = i;
}
int p1 = 0, p2 = 0;
for(int i = 1; i <= n; i++) {
if(n1[i].ls != n2[i].ls || n1[i].rs != n2[i].rs) {
if(!p1) p1 = i;
else p2 = i;
}
}
if(!p1 && !p2) {
puts("0");
return 0;
}
puts("1");
int ans = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(i != j && fa1[i] == j && fa2[j] == i) {
ans = j;
break;
}
}
}
cout << ans << endl;
return 0;
}
I. 智乃的密码
链接:https://ac.nowcoder.com/acm/contest/23478/I
来源:牛客网
题目描述
智乃去注册账号,他发现网站的的密码必须符合以下几个条件
- 密码是仅包含大小写英文字母、数字、特殊符号的字符串。
- 密码的长度不少于LL个字符,并且不多于RR个字符。
- 密码中应该至少包括①大写英文字母、②小写英文字母、③数字、④特殊符号这四类字符中的三种。
所谓特殊字符,是指非大小写字母、数字以及空格回车等不可见字符的可见字符,包括但不限于"~!@#$%^&*()_+"。
现在智乃有一个长度大小为NN的字符串SS,她想知道SS串中有多少个子串是一个符合条件的密码,请你帮助智乃统计符合条件的密码数目。
子串是指字符串中某一段连续的区间,例如对于字符串"abcde"来说,"abc","cde"都是它的子串,而"ace"不是它的子串。
输入描述:
第一行输入三个正整数N,L,R(1≤N≤105,1≤L≤R≤N)N,L,R(1≤N≤105,1≤L≤R≤N),表示SS串的长度,合法密码长度应该在LL到RR个字符之间。
接下来一行输入一个长度为NN的字符串SS,字符串仅包括①大写英文字母、②小写英文字母、③数字、④特殊符号四类字符。
输出描述:
仅一行一个整数,表示有多少子串是一个合法的密码。
示例1
输入
复制
10 6 8
asdfeg111*
输出
复制
3
说明
"eg111*","feg111*","dfeg111*"
这个题双指针或二分都可以做,比赛时写了半天错的双指针改不出来,没办法只能二分了...
二分时遍历每个可能的右端点,二分的是实际区间右端点到左端点的距离(因为距离小的时候满足,则距离大的时候一定满足,解满足单调性),判断区间是否合法可以用前缀和。举例来说,设有区间1 2 3 4 5 6,L = 3, R = 6, 如果[1 2 3 4]合法,那么[1 2 3 4 5]和[1 2 3 4 5 6]肯定都合法。
注意左端点不会超过1!一定要特别处理!代码比较抽象,简单参考一下吧~
#include <iostream>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#define int long long
#define ll long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
int n, L, R;
char s[200005];
int sum[200005][4] = { 0 };
bool check(int mid, int i) {
int a = sum[i][0] - sum[mid - 1][0];
if(a) a = 1;
int b = sum[i][1] - sum[mid - 1][1];
if(b) b = 1;
int c = sum[i][2] - sum[mid - 1][2];
if(c) c = 1;
int d = sum[i][3] - sum[mid - 1][3];
if(d) d = 1;
if(a + b + c + d >= 3) return 1;
else return 0;
}
signed main() {
cin >> n >> L >> R;
scanf("%s", s + 1);
int digit = 0, upper = 0, lower = 0, spe = 0;
int lft = 1;
int ans = 0;
int cnt = 0;
for(int i = 1; i <= n; i++) {
for(int j = 0; j < 4; j++) {
sum[i][j] = sum[i - 1][j];
}
if(s[i] >= '0' && s[i] <= '9') sum[i][0]++;
else if(s[i] >= 'a' && s[i] <= 'z') sum[i][1]++;
else if(s[i] >= 'A' && s[i] <= 'Z') sum[i][2]++;
else sum[i][3]++;
if(i < L) continue;
int l = 0, r = min(R, i) - L, mid;
while(l < r) {
mid = (l + r) >> 1;
if(check(i - (mid + L) + 1, i)) r = mid;
else l = mid + 1;
}
if(check(i - (l + L) + 1, i)) ans += (min(R, i) - (L + l) + 1);//
}
cout << ans;
return 0;
}
// 10 3 5
// a1******1a
// 5 5 5
// a*111
L. 智乃的数据库
链接:https://ac.nowcoder.com/acm/contest/23478/L
来源:牛客网
题目描述
智乃最近在学习数据库的查询语言SQL。SQL (Structured Query Language:结构化查询语言) 是用于管理关系数据库管理系统(RDBMS)。 SQL 的范围包括数据插入、查询、更新和删除,数据库模式创建和修改,以及数据访问控制。
使用SQL,可以灵活的对数据库表进行操作,在数据库的查询语句中有"GROUP BY"这样一种关键字。
GROUP BY 语句用于结合聚合函数,根据一个或多个列对结果集进行分组。
例如数据库中储存了这样一个数据库表Table,执行带有"GROUP BY"关键字的查询语句。
id name val 1 chino 1 2 chino
2 3 kokoa
3 SELECT COUNT(*) FROM Table GROUP BY name;
SQL语句中COUNT表示聚合后每一个组中有多少条数据。
当执行上述的SQL查询语句后,其返回的结果如下
2 2 1
第一行的2表示按照name字段进行分组聚合,一共可以分出2组。
第二行的两个整数表示每组中各有多少条数据。
当然了,"GROUP BY"关键字是可以选中多个列进行分组聚合的,只需要把这些字段用逗号隔开即可。
SELECT COUNT(*) FROM Table GROUP BY name,val;
例如这样的SQL查询语句,执行后返回的结果如下
3 1 1 1
现在智乃把这张数据库表导出给你,请你执行一个SELECT COUNT(*) FROM Table GROUP BY ...;的查询语句,请你把查询的结果告诉智乃。
为了简化问题,我们假设数据库的表由NN条记录以及MM个字段组成,且这些字段的类型均为int类型。
输入描述:
第一行输入两个正整数N,M(1≤N,M≤1000)N,M(1≤N,M≤1000),表示数据库表中有NN条记录以及MM个字段。
接下来一行输入MM个字符串Si(1≤∣Si∣≤50)Si(1≤∣Si∣≤50)表示每个字段的名称。
接下来NN行,每行MM列,输入一个二维表格datai,j(0≤datai,j≤109)datai,j(0≤datai,j≤109)表示数据库表中第ii条记录的第jj个字段的值。
接下来输入一行一个字符串SQL(1≤∣SQL∣≤5×104)SQL(1≤∣SQL∣≤5×104),表示查询语句,查询语句的格式为"SELECT COUNT(∗) FROM Table GROUP BY ...;""SELECTCOUNT(∗)FROMTableGROUPBY...;"。
其中"...""..."是若干个字段名称,保证是之前输入的MM个字段中的某些字段,且这若干个字段互不相同,字段与字段之间用一个逗号隔开。
输出描述:
第一行输出一个整数KK表示聚合后组的数目。
接下来一行输出KK个整数,表示每组中聚合的记录数目,整数与整数之间用空格隔开。
你可以按照你喜欢的顺序输出答案,裁判程序将忽略输出顺序的影响。
示例1
输入
复制
4 3
id name val
1 1 2
1 1 3
1 2 1
2 2 2
SELECT COUNT(*) FROM Table GROUP BY name,id;
输出
复制
3
1 2 1
模拟题。思路就是按这若干个字段进行排序,然后相邻的这若干个字段值相同的分到一组即可。
#include <iostream>
#include <map>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <set>
#include <vector>
#define ll long long
#define pb push_back
#define pii pair<int,int>
using namespace std;
struct node {
int data[1005];
} p[1005];
int rela[1005], cnt = 0;
bool cmp(node a, node b) {
for(int i = 1; i <= cnt; i++) {
if(a.data[rela[i]] == b.data[rela[i]]) continue;
else return a.data[rela[i]] < b.data[rela[i]];
}
return a.data[rela[cnt]] < b.data[rela[cnt]];
}
int n, m;
string s[1005];
string sen;
map<string, int> mp;
int main() {
cin >> n >> m;
for(int i = 1; i <= m; i++) {
cin >> s[i];
mp[s[i]] = i;
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cin >> p[i].data[j];
}
}
getchar();
getline(cin, sen);
int pos = sen.find("BY");
pos += 3;
int lst = pos;
for(int i = pos; i < sen.size(); i++) {
if(sen[i] == ',' || sen[i] == ';') {
string tmp = sen.substr(lst, i - lst);
lst = i + 1;
cnt++;
rela[cnt] = mp[tmp];
}
}
sort(p + 1, p + n + 1, cmp);
int num = 1;
vector<int> ans;
for(int i = 2; i <= n + 1; i++) {
bool diff = 0;
for(int j = 1; j <= cnt; j++) {
if(p[i].data[rela[j]] != p[i - 1].data[rela[j]]) {
diff = 1;
break;
}
}
if(diff) {
ans.push_back(num);
num = 1;
} else {
num++;
}
}
cout << ans.size() << endl;
for(int i = 0; i < ans.size() - 1; i++) {
cout << ans[i] << " ";
}
cout << ans[ans.size() - 1];
return 0;
}