活动介绍

``` #define _CRT_SECURE_NO_WARNINGS #include<cstdio> #include <vector> #include <string> #include<cstring> using namespace std; using ll = long long; int tree[1000000][10]; int last[1000000]; int cnt = 0; int yes; void inset(char* a) { int now = 0; yes = 0; int temp = cnt; for (int i = 0; i < strlen(a); i++) { if (!tree[now][a[i] - '0']) { tree[now][a[i] - '0'] = ++cnt; } now = tree[now][a[i] - '0']; if (last[now] != 0)yes = 1; } if (temp == cnt)yes = 1; last[now] = strlen(a); } void renew() { memset(tree, 0, sizeof(tree)); memset(last, 0, sizeof(last)); } int main() { int n; scanf("%d", &n); while (n--) { int m; scanf("%d", &m); renew(); for (int i = 0; i < m; i++) { char a[1000]; scanf("%s", a); inset(a); if (yes) { printf("NO\n"); yes = 2; break; } } if (yes == 0)printf("YES\n"); } }```哪里有问题

时间: 2025-07-10 12:36:44 浏览: 7
<think>嗯,我现在要仔细看看用户提供的这段C++代码,找出其中的问题。让我先通读一遍代码,理解它的功能,然后逐步分析可能存在的问题。 首先,这段代码看起来像是一个处理电话号码或者字符串前缀问题的程序。可能是在检查一组字符串中是否存在某个字符串是另一个的前缀,比如经典的“没有号码是其他号码的前缀”的问题。如果是这样的话,正确的输出应该是当所有号码都不互为前缀时输出“YES”,否则输出“NO”。 代码的结构大致如下: - 包含必要的头文件,定义了一些全局变量,包括一个二维数组`tree`,可能作为字典树(Trie)的结构,`last`数组记录每个节点是否是某个字符串的结尾,`cnt`作为节点计数器,`yes`作为标记是否存在问题。 - `inset`函数用于插入字符串到字典树中,并检查是否存在前缀冲突。 - `renew`函数在每次测试用例开始时重置字典树。 - `main`函数处理多个测试用例,每个测试用例读取多个字符串,逐个插入到字典树中,检查是否存在问题。 接下来,我需要分析可能的问题所在。 首先看`inset`函数: 函数参数是`char* a`,然后遍历每个字符。对于每个字符,如果当前节点的子节点不存在,则创建新节点(`cnt`递增)。然后移动到子节点,并检查该子节点是否已经被标记为某个字符串的结尾(`last[now] != 0`),如果是,说明当前插入的字符串的前缀已经存在,设置`yes=1`。 循环结束后,检查是否创建了新节点,如果没有(即`temp == cnt`),说明当前字符串是之前某个字符串的前缀,设置`yes=1`。 这里可能的问题在于,当插入的字符串是之前某个字符串的前缀时,比如已经插入了"123",现在插入"12",那么在插入第二个字符的时候,会到达节点now,此时如果该节点的`last[now]`已经被设置,则`yes=1`。但循环结束后,如果temp等于cnt,说明没有创建新节点,即整个字符串都是已有路径,这时候该字符串是之前某个字符串的前缀,因此也应该设置`yes=1`。这部分的逻辑是正确的。 不过,需要注意,在插入过程中,如果在中间某个节点发现`last[now] != 0`,则当前插入的字符串有一个前缀已经存在,即存在比当前字符串短的字符串作为前缀,这时应该标记yes。而如果整个字符串插入完毕,发现没有创建新节点(即temp等于cnt),说明当前字符串完全覆盖了之前的某个字符串的路径,即当前字符串是之前某个字符串的前缀。例如,之前插入"1234",现在插入"123",则在插入第三个字符时,路径已存在,但此时最后一个节点的`last`可能未被设置,所以插入完成后,如果temp等于cnt,那么说明整个字符串没有新增节点,但此时需要检查最后一个节点是否被标记为某个字符串的结尾。例如,如果之前插入的是"123",现在插入的是"123",则此时temp等于cnt,并且最后的节点已经被标记,所以yes会被设置为1。 但这里可能存在一个问题:当插入一个已经存在的字符串时,比如两次插入同样的字符串,这时在循环过程中,每个字符的节点都已存在,所以不会创建新节点。循环结束后,temp等于cnt,yes被设置为1。这时候会被视为存在冲突,但实际上,重复插入相同的字符串是否算作错误?这取决于问题的要求。例如,在电话号码问题中,相同的号码是不允许的,所以此时应该返回“NO”。如果问题要求所有字符串互不为前缀,并且不允许重复,那么这确实是一个问题。但如果允许重复的字符串,那么可能存在问题。 但原代码中的逻辑是,当插入的字符串与之前的某个字符串完全相同,那么循环结束后,temp等于cnt,yes被设置为1。此时会被视为存在冲突。这可能正确,也可能不正确,具体取决于题目要求。比如,如果题目允许重复,那么这里就会误判。但假设题目不允许重复,那么这部分是正确的。 接下来看main函数: 每次测试用例开始时调用`renew`来重置tree和last数组。然后读取m个字符串,逐个插入,如果在插入过程中发现yes被设置为1,则输出“NO”并设置yes=2,然后break循环。处理完所有字符串后,如果yes是0,输出“YES”。 这里可能存在的一个问题是,当处理多个测试用例时,`cnt`变量没有被重置。每次调用`renew`时,会重置tree和last数组,但`cnt`仍然保持之前的值。例如,第一个测试用例使用了cnt增加到某个值,第二个测试用例开始时,tree和last被memset为0,但cnt并没有重置为0。这将导致后续插入时,新分配的节点可能覆盖之前测试用例的节点,但因为tree和last已经被清空,但cnt没有重置,这会导致新的测试用例中节点的索引错误。 例如,第一个测试用例结束后,cnt是100。第二个测试用例开始,调用renew,此时tree数组被清空,但cnt还是100。当插入第一个字符串时,会从0开始分配节点,但在创建新节点时,tree[now][...] = ++cnt,即cnt变成101,而之前的tree[0][...]可能被设置为101,但此时tree数组的大小是1000000,可能不会溢出,但节点的索引会变得很大,可能超过初始预期,导致后续操作访问到未初始化的内存区域,或者覆盖其他数据。 这显然是一个严重的问题。每个测试用例应该独立使用字典树,因此必须在每个测试用例开始时将cnt重置为0。而原代码中的renew函数并没有重置cnt,导致不同测试用例之间共享cnt的值,从而造成节点分配错误。 这是第一个问题:在多个测试用例的情况下,cnt没有被重置,导致后续测试用例的字典树结构错误。 第二个问题是在`inset`函数中,检查`last[now]`的时间点可能存在问题。例如,当插入一个较长的字符串后,再插入一个较短的字符串,此时在插入到较短字符串的最后一个字符时,该节点的`last`可能未被之前的字符串设置。例如,假设先插入"1234",此时在插入到第四个字符时,设置last[now]=4。然后插入"123",在插入第三个字符时,检查该节点的last是否为非零。但此时,该节点的last可能未被设置,因为之前的字符串是"1234",而第三个字符所在的节点可能未被标记为结束。因此,在这种情况下,插入"123"时,在循环过程中不会检测到冲突,但循环结束后,由于没有创建新节点(temp等于cnt),所以会设置yes=1,此时是正确的,因为"123"是"1234"的前缀吗?不,实际上是"1234"是"123"的前缀吗?不是,"123"是"1234"的前缀,所以当插入"123"时,如果在插入第三个字符后,该节点的last未被设置,那么循环结束后,由于没有创建新节点,就会设置yes=1,此时正确识别出当前插入的字符串是之前某个字符串的前缀(这里的情况是,当前插入的字符串是之前插入的某个字符串的前缀吗?比如,之前插入的是"1234",现在插入的是"123",那么当前的字符串是之前字符串的前缀,所以应该返回冲突。但根据代码逻辑,在插入"123"时,循环结束后,temp等于cnt吗? 假设在插入第一个字符串"1234"时,cnt初始为0,每次创建新节点,cnt增加到4。插入第二个字符串"123"时,temp初始是4。在循环中,每个字符对应的节点已经存在,所以不会创建新节点。循环结束后,temp等于cnt(4),所以yes被设置为1。此时正确检测到当前字符串是之前某个字符串的前缀,所以输出“NO”。这没有问题。 但是如果反过来,先插入"123",再插入"1234",那么在插入"1234"时,当处理到第三个字符时,该节点的last已经被设置为3(由第一个字符串),此时yes会被设置为1,因为检测到当前节点已被标记为结束,即存在一个字符串作为当前插入字符串的前缀。这样就会正确识别出问题。 所以,这部分逻辑是正确的。 那回到问题,主要的错误是每次测试用例开始时没有将cnt重置为0,导致后续测试用例的节点分配错误。 此外,其他潜在的问题可能包括: 1. 在`inset`函数中,每次插入字符串时,都使用`strlen(a)`在循环条件中,这可能导致性能问题,因为`strlen`是O(n)的时间复杂度,而每次循环都调用一次。应该提前计算长度,保存到变量中。 例如,将循环改为: int len = strlen(a); for (int i=0; i < len; i++) { ... } 否则,每次循环都计算一次strlen(a),时间复杂度是O(m^2)对于长度为m的字符串。不过,对于题目中的测试用例来说,可能不会超时,但这是一个优化点,不是错误。 不过,用户的问题是关于代码哪里有问题,可能这个性能问题不算错误,但属于可以改进的地方。 另一个可能的问题是,在插入字符串时,当yes被设置为1后,可能继续处理后面的字符。例如,在插入字符串的过程中,如果发现某个中间节点是结束节点,设置yes=1,但循环仍然继续,移动到子节点。这在逻辑上没有问题,但可以提前终止循环,节省时间。不过不影响正确性。 另外,在main函数中,处理每个测试用例时,当yes被设置为1后,会输出“NO”并设置yes=2,然后break循环。但后续的字符串仍然会被读取吗?例如,假设输入m=5,当第三个字符串导致yes=1,输出“NO”后break,跳出当前循环,剩下的两个字符串不会被处理。这是正确的,因为一旦发现冲突,就可以立即停止处理当前测试用例。这部分没有问题。 现在总结问题: 最大的错误是在处理多个测试用例时,没有重置cnt变量。每个测试用例开始的时候,应该将cnt重置为0,因为每个测试用例的字典树是独立的。当前代码中,只在renew函数中清除了tree和last数组,但cnt没有被重置,导致后续测试用例的节点索引错误。 例如,第一个测试用例可能创建了cnt=100的节点,第二个测试用例开始时,tree和last被memset为0,但cnt仍然是100,此时插入字符串时会从cnt=100开始分配节点,而tree数组已经被清空,所以新分配的节点会覆盖之前的内存,导致不可预测的行为。 因此,正确的做法是在每个测试用例开始时,将cnt重置为0。这需要在renew函数中重置,或者在处理每个测试用例时重置。 另一个可能的问题是,在`inset`函数中,当插入一个字符串后,如果该字符串是之前某个字符串的前缀(例如,当前插入的字符串是之前字符串的前缀),此时temp等于cnt,所以yes被设置为1。但是,在这种情况下,必须确保最后一个节点的`last`是否已经被设置。例如,假设之前插入的是"12",现在插入的是"123"。在插入"123"的过程中,当处理到第三个字符时,该节点可能未被标记为结束,所以循环中的检查不会触发yes=1。循环结束后,因为创建了新节点(假设在插入第三个字符时,该节点不存在,所以cnt会增加),所以temp(初始为cnt_before_insert)不等于cnt_after_insert,因此不会触发yes=1。但此时,当前插入的字符串比之前的字符串长,所以之前的字符串是当前字符串的前缀,这种情况下应该被检测出来。然而,原代码在这种情况下可能无法检测到。 比如,假设先插入"12",再插入"123"。插入"12"时,在最后一个字符的节点设置last[now]=2。插入"123"时,前两个字符的路径已经存在,第三个字符需要创建新节点。此时循环结束后,temp等于原来的cnt,假设插入第三个字符时创建了新节点,所以temp != cnt,因此不会设置yes=1。但是,此时,原来的"12"是当前字符串"123"的前缀,因此应该被视为冲突。但原代码在这种情况下无法检测到,导致错误地输出“YES”。 这表明原代码存在逻辑错误:当当前插入的字符串是之前某个字符串的超集(即之前字符串是当前字符串的前缀)时,无法检测到冲突。 例如,测试用例: 1 2 12 123 正确的输出应该是“NO”,因为"12"是"123"的前缀。但根据原代码的逻辑,在插入"123"时,前两个字符的节点已经存在。在插入第三个字符时,创建新节点,所以cnt会增加。循环结束后,temp(插入前的cnt)不等于插入后的cnt,所以不会设置yes=1。同时,在插入过程中,检查每个节点的last是否被设置,当处理到第三个字符的节点时,该节点之前可能未被标记为结束,所以yes保持0。因此,整个插入过程不会触发yes=1,导致最后输出“YES”,而正确答案应为“NO”。 这说明原代码存在逻辑错误,无法检测到当前插入的字符串包含之前某个字符串作为前缀的情况。 问题出在插入过程中,只有当当前节点的last被设置时才触发yes=1。而这种情况发生在当前插入的字符串在路径上遇到了某个结束节点。例如,插入较长的字符串时,路径上存在某个较短的字符串的结束节点。但如果插入的是较长的字符串,而之前存在一个较短的字符串作为前缀,那么在插入较长字符串的过程中,不会经过较短的字符串的结束节点(假设较短的字符串的结束节点在路径的中间),所以在插入过程中不会触发yes=1。而循环结束后,只有当没有创建新节点时(即当前字符串是之前某个字符串的前缀)才会触发yes=1。但在这个例子中,较长字符串的插入会创建新节点,所以循环结束后的检查不会触发yes=1。因此,原代码无法检测到这种情况,导致错误。 因此,原代码存在两个问题: 1. cnt未在每个测试用例开始时重置,导致多个测试用例之间的干扰。 2. 无法检测到当前插入的字符串包含之前某个字符串作为前缀的情况(即较长的字符串包含较短的字符串作为前缀的情况)。 如何修正这两个问题? 对于第一个问题,在每次测试用例开始时,将cnt重置为0。可以在renew函数中添加`cnt = 0;`,或者在处理每个测试用例时重置。 对于第二个问题,需要在插入字符串结束时,检查当前节点是否有子节点,或者在该节点之后是否还有其他节点,即当前插入的字符串是否是某个更长字符串的前缀。或者,在插入字符串的最后一个字符的节点后,检查该节点是否有任何子节点存在。如果有,说明存在某个字符串以当前字符串为前缀,需要触发yes=1。 例如,在插入字符串的最后,检查该节点是否有任何子节点。如果有,则说明之前存在更长的字符串,当前字符串是它们的前缀,因此应该设置yes=1。 但如何实现这一点? 在当前的代码中,当插入字符串完成后,该节点被标记为last[now] = strlen(a)。此时,如果该节点存在任何子节点,说明之前插入的某个字符串比当前字符串长,并且以当前字符串为前缀。例如,先插入"1234",再插入"123",那么在插入"123"时,最后的节点是第三个字符的节点,此时该节点可能已经有子节点(即第四个字符的节点),因此应该触发yes=1。 因此,在插入字符串的最后,需要检查该节点是否有任何子节点。如果有,则说明存在更长的字符串以当前字符串为前缀,因此当前插入的字符串是这些更长字符串的前缀,从而导致冲突。 因此,在插入字符串的最后,除了检查是否创建了新节点(即是否是已有字符串的前缀)之外,还需要检查当前节点是否存在子节点(即是否有更长的字符串以当前字符串为前缀)。 例如,在插入字符串的末尾,执行以下步骤: - 如果该节点已经被标记为结束(即last[now] !=0),说明当前字符串是之前某个字符串的前缀,触发yes=1。 - 如果该节点有子节点,说明存在更长的字符串以当前字符串为前缀,触发yes=1。 但原代码中,在循环过程中检查的是每个节点的last是否为非零,这可以检测到当前字符串有一个前缀已经存在。但在循环结束后,仅检查是否创建了新节点,这只能检测到当前字符串是否是之前某个字符串的前缀,但无法检测到是否有更长的字符串以当前字符串为前缀。 因此,修正的方法是在插入字符串的末尾,检查当前节点是否有任何子节点。如果有,则说明存在更长的字符串以当前字符串为前缀,从而触发冲突。 因此,修改inset函数的结尾部分: 在循环结束后,执行以下操作: if (temp == cnt) { yes = 1; // 当前字符串是已有字符串的前缀 } 此外,还需要检查当前节点是否有任何子节点: for (int i = 0; i < 10; ++i) { if (tree[now][i] != 0) { yes = 1; // 存在更长的字符串以当前字符串为前缀 break; } } 例如,假设之前插入的是"1234",现在插入的是"123"。在插入"123"后,当前节点(第三个字符的节点)的子节点中存在第四个字符的节点,因此触发yes=1。 这将正确检测到这种情况。 原代码中在循环结束后仅检查了temp是否等于cnt,这只能处理当前字符串是已有字符串的前缀的情况,但无法处理当前字符串是其他字符串的前缀的情况。因此,必须补充检查当前节点是否有子节点。 因此,原代码的第二个问题在于,没有在插入字符串的末尾检查当前节点是否有子节点,导致无法检测到当前字符串作为前缀的情况。 总结问题: 1. cnt变量未在每个测试用例开始时重置,导致跨测试用例的节点分配错误。 2. inset函数没有在插入完成后检查当前节点是否有子节点,导致无法检测到当前字符串是其他更长字符串的前缀的情况。 修正方法: 对于问题1,在renew函数中添加cnt = 0; 或者在每个测试用例开始时重置cnt。 对于问题2,在inset函数的末尾,检查当前节点是否有子节点。如果有,则设置yes = 1。 另外,原代码中,在循环过程中,如果发现当前节点的last不为零,则设置yes=1。例如,当插入"1234"后,插入"123456",则在插入到第四个字符时,该节点的last被设置为4,触发yes=1。这正确检测到当前字符串有一个前缀已经存在。 现在,针对用户提供的代码,这两个问题是存在的。 除此之外,可能还有其他小问题,比如: - 数组的大小是1000000,可能不够,导致溢出。但假设题目中的数据范围在此之内,可能没有问题。 - 在inset函数中,last[now] = strlen(a); 这里设置的是字符串的长度,这可能没有问题,但通常字典树的结束标记可以是一个布尔值。但原代码中的逻辑是用last[now]来标记是否为某个字符串的结尾,可能不影响,只要非零即可。 例如,当插入字符串a时,在最后一个字符对应的节点now,设置last[now] = strlen(a),这样当其他字符串经过该节点时,检测到last[now] != 0,就会触发冲突。 这可能没问题,只要存储的是非零值即可。 综上,用户提供的代码存在两个主要问题: 1. 多次测试用例时,cnt未重置,导致节点分配错误。 2. 插入字符串后未检查当前节点是否有子节点,导致无法检测到当前字符串作为前缀的情况。 因此,修正后的代码应该: - 在renew函数中重置cnt为0。 - 在inset函数的末尾,检查当前节点是否有子节点,如果有,则设置yes=1。 修正后的代码可能如下: 在renew函数中添加: cnt = 0; 在inset函数的循环结束后,添加: for (int i = 0; i < 10; i++) { if (tree[now][i] != 0) { yes = 1; break; } } 或者,在循环结束后,无论temp是否等于cnt,都检查当前节点是否有子节点。 此外,原代码中在插入字符串的末尾设置last[now] = strlen(a),这可能正确,但需要确保在设置之前,如果该节点已经被设置过,是否会产生影响。例如,插入相同的字符串两次,第一次设置last[now]为长度,第二次再次设置,这没问题,但此时yes已经被设置为1,因为在循环过程中,当插入到最后一个字符时,该节点的last已经被设置,从而触发yes=1。 综上,用户代码的问题主要在于上述两点。</think>该代码存在两个主要问题: ### 1. **未重置Trie树节点计数器(`cnt`)** 在多个测试用例运行时,`cnt`变量未初始化。每次执行新测试用例时,`tree`数组通过`memset`清空,但`cnt`仍保留前一次的值,导致后续插入时节点索引错误。 **修复方法**:在`renew()`函数中添加`cnt = 0;`。 ### 2. **无法检测长字符串包含短字符串作为前缀的情况** 当插入一个较长字符串(如"1234")后,再插入其前缀(如"123"),代码无法正确识别后者是前者的前缀。原逻辑仅检查插入路径中是否存在结束节点,但未在插入完成后检查当前节点是否有子节点。 **修复方法**:在`inset()`函数末尾添加对子节点的检查。 --- ### 修正后的代码 ```cpp #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <vector> #include <string> #include <cstring> using namespace std; using ll = long long; int tree[1000000][10]; int last[1000000]; int cnt = 0; int yes; void renew() { memset(tree, 0, sizeof(tree)); memset(last, 0, sizeof(last)); cnt = 0; // 修复1:重置节点计数器 } void inset(char* a) { int now = 0; yes = 0; int temp = cnt; int len = strlen(a); // 优化:避免重复计算长度 for (int i = 0; i < len; i++) { if (!tree[now][a[i] - '0']) { tree[now][a[i] - '0'] = ++cnt; } now = tree[now][a[i] - '0']; if (last[now]) yes = 1; // 发现前缀冲突 } // 修复2:检查当前节点是否有子节点 for (int i = 0; i < 10; i++) { if (tree[now][i]) { yes = 1; break; } } if (temp == cnt) yes = 1; // 当前字符串是已有字符串的前缀 last[now] = len; // 标记当前节点为字符串结尾 } int main() { int n; scanf("%d", &n); while (n--) { int m; scanf("%d", &m); renew(); for (int i = 0; i < m; i++) { char a[1000]; scanf("%s", a); inset(a); if (yes) { printf("NO\n"); yes = 2; break; } } if (yes == 0) printf("YES\n"); } } ``` ### 关键修改说明 1. **`renew()`函数重置`cnt`** 每个测试用例开始时,将`cnt`归零,确保字典树节点索引正确。 2. **插入后检查子节点** 在`inset()`末尾遍历当前节点的所有可能子节点(数字0-9),若存在子节点,则说明当前字符串是其他更长字符串的前缀,触发冲突。 3. **优化字符串长度计算** 将`strlen(a)`提前计算保存,避免循环中重复调用。 --- ### 功能验证 - **测试用例1** 输入: ``` 1 2 123 1234 ``` 输出应为`NO`,因"123"是"1234"的前缀。修正后的代码能正确检测。 - **测试用例2** 输入: ``` 2 2 123 456 2 1234 123 ``` 第一个测试用例输出`YES`,第二个输出`NO`。修正后的代码能正确处理多个测试用例。
阅读全文

相关推荐

#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <vector> #include <algorithm> #include <unordered_map> #include <tuple> #include <queue> #include <string> #include <cstring> #include<cstdio> using namespace std; using ll = long long; #define N 10000005 int trie[N][4], fail[N], lens[N]; char cm[100005][100]; bool vis[N]; int cnt = 0, ans = 0, root = 0; int calc(char a) { if (a == 'N')return 0; else if (a == 'S')return 1; else if (a == 'W')return 2; else return 3; } void insert(char* a) { int now = root; int n = strlen(a); for (int i = 0; a[i] != '\0'; i++) { int bit = calc(a[i]); if (!trie[now][bit])trie[now][bit] = ++cnt; now = trie[now][bit]; } lens[ans] = n; } void buildfail() { queue<int>line; fail[root] = root; for (int i = 0; i < 4; i++) { if (trie[root][i]) { fail[trie[root][i]] = root; line.push(trie[root][i]); } } while (line.size()) { int now = line.front(); line.pop(); for (int i = 0; i < 4; i++) { if (!trie[now][i])trie[now][i] = trie[fail[now]][i]; else { fail[trie[now][i]] = trie[fail[now]][i]; line.push(trie[now][i]); } } } } void search(string a) { int now = root; for (int i = 0; i < a.size(); i++) { int index = calc(a[i]); now = trie[now][index]; int temp = now; while (temp != root) { if (!vis[temp]) vis[temp] = true; else break; temp = fail[temp]; } } } int nizhao(char* a) { int now = root; int res = 0; for (int i = 0; a[i] != '\0'; i++) { int bit = calc(a[i]); if (vis[trie[now][bit]])res++; else break; now = trie[now][bit]; } return res; } int main() { int n, m; cin >> n >> m; string mu; cin >> mu; for (int i = 0; i < m; i++) { scanf("%s", cm[i]); insert(cm[i]); } buildfail(); search(mu); for (int i = 0; i < m; i++) cout << nizhao(cm[i]) << endl; }哪里有问题

#define _CRT_SECURE_NO_WARNINGS #include <vector> #include <algorithm> #include <unordered_map> #include <tuple> #include <queue> #include <string> #include <cstring> #include<cstdio> using namespace std; using ll = long long; #define N 10000005 int trie[N][4], fail[N]; char cm[100005][100],t[N]; bool vis[N]; int n, m; int cnt = 0, root = 0; int calc(char a) { if (a == 'N')return 0; else if (a == 'S')return 1; else if (a == 'W')return 2; else return 3; } void insert(char* a) { int now = root; int n = strlen(a); for (int i = 0; a[i] != '\0'; i++) { int bit = calc(a[i]); if (!trie[now][bit])trie[now][bit] = ++cnt; now = trie[now][bit]; } } void buildfail() { queue<int>line; fail[root] = root; for (int i = 0; i < 4; i++) { if (trie[root][i]) { fail[trie[root][i]] = root; line.push(trie[root][i]); } } while (line.size()) { int now = line.front(); line.pop(); for (int i = 0; i < 4; i++) { if (!trie[now][i])trie[now][i] = trie[fail[now]][i]; else { fail[trie[now][i]] = trie[fail[now]][i]; line.push(trie[now][i]); } } } } void search() { int now = root; for (int i = 0; t[i]!='\0'; i++) { int index = calc(t[i]); now = trie[now][index]; int temp = now; while (temp != root) { if (!vis[temp]) { vis[temp] = true; break; } temp = fail[temp]; } } } int nizhao(char* a) { int now = root; int res = 0; for (int i = 0; a[i] != '\0'; i++) { int bit = calc(a[i]); now = trie[now][bit]; if (vis[now])res++; } return res; } int main() { scanf("%d%d", &n, &m); scanf("%s", t); for (int i = 0; i < m; i++) { scanf("%s", cm[i]); insert(cm[i]); } buildfail(); search(); for (int i = 0; i < m; i++) printf("%d\n", nizhao(cm[i])); }哪里有问题