查找两个字符串是否在O(n)中是字谜-使用XOR的解决方案

时间:2018-10-05 21:19:43

标签: c++ algorithm

我正在处理hackerearth

中的问题

目标是确定输入字符串是否为O(n)时间的字谜。

输入格式:

  • 第一行包含一个整数T,表示编号no。测试用例。
  • 每个测试由一行组成,每行包含两个空格 长度相等的字符串S1和S2。

我的代码:

#include <iostream>
#include <string>

int main()
{
    int T;
    std::cin >> T;
    std::cin.ignore();

    for(int i = 0; i < T; ++i)
    {
        std::string testString;
        std::getline(std::cin, testString);

        char test =  ' ';

        for (auto& token : testString)
        {
            if(token != ' ')
                test ^= token;
        }

        if (test == ' ')
            std::cout << "YES\n";
        else
            std::cout << "NO\n";
    }

}

上面的代码未通过5/6 hackerearth测试。 我的错误在哪里?这是解决这个问题的好方法吗?

2 个答案:

答案 0 :(得分:2)

注意:您的问题标题是第二个单词必须是第一个单词的 anagram 。但是,与hackerearth问题有关的链接使用术语 rearranged ,该术语比字谜更具限制性,并且还说:

  

如果字符串S1的排列的 any 等于字符串S2,则两个字符串S1和S2相同。


一种算法是保持传入字符的直方图。

这是通过两个循环完成的,一个循环用于第一个单词,另一个循环用于第二个单词。

对于第一个单词,逐个字符进行处理并增加直方图的值。通过保持运行计数来计算第一个单词的长度。

到达空间后,执行另一个循环以减小直方图。保持达到零的直方图单元数的计数。最后,这必须与第一个单词的长度(即成功)匹配。

在第二个循环中,如果直方图单元格变为负数,则这是不匹配的,因为第二个单词的第一个单词中没有字符或第一个单词中有太多字符。


注意事项:对于这是一个类似于C的解决方案,我深表歉意,但可以轻松地使其适应于使用更多的STL组件

一次输入一次字符可能比将整行读入缓冲区字符串要快

编辑:我已在代码示例中添加了注释/注释,以使内容更加清晰

#include <stdio.h>
#include <stdlib.h>

char buf[(200 * 1024) + 100];

void
dotest(FILE *xf)
{
    int histo[26] = { 0 };
    int len = 0;
    int chr;
    int match = 0;
    int fail = 0;
    int cnt;

    // scan first word
    while (1) {
        chr = fgetc(xf);

        // stop on delimiter between first and second words
        if (chr == ' ')
            break;

        // convert char to histogram index
        chr -= 'a';

        // increment the histogram cell
        cnt = ++histo[chr];

        // calculate number of non-zero histogram cells
        if (cnt == 1)
            ++len;
    }

    // scan second word
    while (1) {
        chr = fgetc(xf);

        // stop on end-of-line or EOF
        if (chr == '\n')
            break;
        if (chr == EOF)
            break;

        // convert char to histogram index
        chr -= 'a';

        // decrement the histogram cell
        cnt = --histo[chr];

        // if the cell reaches zero, we [seemingly] have a match (i.e. the
        // number of instances of this char in the second word match the
        // number of instances in the first word)
        if (cnt == 0)
            match += 1;

        // however, if we go negative, the second word has too many instances
        // of this char to match the first word
        if (cnt < 0)
            fail = 1;
    }

    do {
        // too many letters in second word that are _not_ in the first word
        if (fail)
            break;

        // the number of times the second word had an exact histogram count
        // against the first word must match the number of chars in the first
        // [and second] word (i.e. all scrambled chars in the second word had
        // a place in the first word)
        fail = (match != len);
    } while (0);

    if (fail)
        printf("NO\n");
    else
        printf("YES\n");
}

// main -- main program
int
main(int argc,char **argv)
{
    char *file;
    FILE *xf;

    --argc;
    ++argv;

    file = *argv;
    if (file != NULL)
        xf = fopen(file,"r");
    else
        xf = stdin;

    fgets(buf,sizeof(buf),xf);
    int tstcnt = atoi(buf);

    for (int tstno = 1;  tstno <= tstcnt;  ++tstno)
        dotest(xf);

    if (file != NULL)
        fclose(xf);

    return 0;
}

更新:

  

我只看了一下代码,但似乎找到的每个字符(字符串长度)都会增加len。并且匹配仅在突出显示唯一的char(直方图元素)时才上升,所以检查匹配== len不好吗?

len仅在 first 循环中递增。 (即)仅是第一个单词的长度(如上述算法说明中所述)。

在第一个循环中,检查char是否为空格[通过输入的问题定义来界定第一个单词的末尾,保证了],并且循环中断了在[之前 len递增]中,所以len是正确的。

使用lenmatchfail可以加快速度。否则,最后,我们必须扫描整个直方图,并确保所有元素都为零才能确定成功/失败(即,任何非零元素都表示不匹配/失败)。

注意:在以前进行这种定时编码挑战时,我注意到它们在经过的时间/速度和空间方面可能非常严格。最好尝试尽可能地优化,因为即使该算法在技术上是正确的,也可能由于使用过多内存或花费太多时间而无法通过测试。

这就是为什么我建议使用字符串缓冲区的原因,因为问题定义的最大大小可以为100,000个字节。另外,在最后进行[不必要的]直方图扫描也会增加时间。


更新#2:

一次读取整行然后使用char指针遍历缓冲区可能更快。这是执行此操作的版本。哪种方法更快,需要尝试/进行基准测试。

#include <stdio.h>
#include <stdlib.h>

char buf[(200 * 1024) + 100];

void
dotest(FILE *xf)
{
    char *cp;
    int histo[26] = { 0 };
    int len = 0;
    int chr;
    int match = 0;
    int fail = 0;
    int cnt;

    cp = buf;
    fgets(cp,sizeof(buf),xf);

    // scan first word
    for (chr = *cp++;  chr != 0;  chr = *cp++) {
        // stop on delimiter between first and second words
        if (chr == ' ')
            break;

        // convert char to histogram index
        chr -= 'a';

        // increment the histogram cell
        cnt = ++histo[chr];

        // calculate number of non-zero histogram cells
        if (cnt == 1)
            ++len;
    }

    // scan second word
    for (chr = *cp++;  chr != 0;  chr = *cp++) {
        // stop on end-of-line
        if (chr == '\n')
            break;

        // convert char to histogram index
        chr -= 'a';

        // decrement the histogram cell
        cnt = --histo[chr];

        // if the cell reaches zero, we [seemingly] have a match (i.e. the
        // number of instances of this char in the second word match the
        // number of instances in the first word)
        if (cnt == 0)
            match += 1;

        // however, if we go negative, the second word has too many instances
        // of this char to match the first word
        if (cnt < 0) {
            fail = 1;
            break;
        }
    }

    do {
        // too many letters in second word that are _not_ in the first word
        if (fail)
            break;

        // the number of times the second word had an exact histogram count
        // against the first word must match the number of chars in the first
        // [and second] word (i.e. all scrambled chars in the second word had
        // a place in the first word)
        fail = (match != len);
    } while (0);

    if (fail)
        printf("NO\n");
    else
        printf("YES\n");
}

// main -- main program
int
main(int argc,char **argv)
{
    char *file;
    FILE *xf;

    --argc;
    ++argv;

    file = *argv;
    if (file != NULL)
        xf = fopen(file,"r");
    else
        xf = stdin;

    fgets(buf,sizeof(buf),xf);
    int tstcnt = atoi(buf);

    for (int tstno = 1;  tstno <= tstcnt;  ++tstno)
        dotest(xf);

    if (file != NULL)
        fclose(xf);

    return 0;
}

更新#3:

以上两个示例都有一个小错误。它将在(例如)aaa aaa的输入行上报告 false 阴性。

len的增量总是在第一个循环中完成。这是不正确的。我已经编辑了以上两个示例,有条件地对len进行了递增(即,如果在递增之前直方图单元格为零,则 )。现在,len是“第一个字符串中非零直方图像元的数量”。这会考虑字符串中的重复项(例如aa)。

正如我提到的,使用lenmatchfail是为了避免最后扫描所有直方图单元的需要,对于非零的单元格,这意味着不匹配/失败。

对于较短的输入行,这可能[运行]更快,直方图的后扫描花费的时间比输入行循环长。

但是,考虑到输入线的长度可以为200k,则概率是[几乎]所有直方图单元都将递增/递减。而且,柱状图的后扫描(例如,检查26个整数数组的值是否为非零)现在在总时间中可以忽略不计。

因此,在下面两个循环中消除len/match计算的简单实现可能是最快/最好的选择。这是因为两个循环稍快一些。

#include <stdio.h>
#include <stdlib.h>

char buf[(200 * 1024) + 100];

void
dotest(FILE *xf)
{
    char *cp;
    char buf[(200 * 1024) + 100];
    int histo[26] = { 0 };
    int chr;
    int fail = 0;

    cp = buf;
    fgets(cp,sizeof(buf),xf);

    // scan first word
    for (chr = *cp++;  chr != 0;  chr = *cp++) {
        // stop on delimiter between first and second words
        if (chr == ' ')
            break;

        // convert char to histogram index
        chr -= 'a';

        // increment the histogram cell
        ++histo[chr];
    }

    // scan second word
    for (chr = *cp++;  chr != 0;  chr = *cp++) {
        // stop on end-of-line
        if (chr == '\n')
            break;

        // convert char to histogram index
        chr -= 'a';

        // decrement the histogram cell
        --histo[chr];
    }

    // scan histogram
    for (int idx = 0;  idx < 26;  ++idx) {
        if (histo[idx]) {
            fail = 1;
            break;
        }
    }

    if (fail)
        printf("NO\n");
    else
        printf("YES\n");
}

// main -- main program
int
main(int argc,char **argv)
{
    char *file;
    FILE *xf;

    --argc;
    ++argv;

    file = *argv;
    if (file != NULL)
        xf = fopen(file,"r");
    else
        xf = stdin;

    fgets(buf,sizeof(buf),xf);
    int tstcnt = atoi(buf);

    for (int tstno = 1;  tstno <= tstcnt;  ++tstno)
        dotest(xf);

    if (file != NULL)
        fclose(xf);

    return 0;
}

缺点是第二个循环中没有 “提前逃脱”。即使我们可能能够尽早告知第二个字符串不匹配(例如),我们也必须完成第二个字符串的扫描:

aaaaaaaaaa baaaaaaaaa
baaaaaaaaa bbaaaaaaaa

使用简单版本,即使我们知道当看到b(即直方图单元格为负)并跳过对扫描的扫描时第二个字符串永远都不会匹配,我们无法提早终止第二个循环。第二个单词中有多个a

因此,这是一个具有上述简单第一个循环的版本。它会添加动态检查,以检查第二个循环中电池是否变为负数。

再一次,[我介绍的四个]中哪个版本是最好的,需要进行一些实验/基准测试。

#include <stdio.h>
#include <stdlib.h>

char buf[(200 * 1024) + 100];

void
dotest(FILE *xf)
{
    char *cp;
    int histo[26] = { 0 };
    int chr;
    int fail = 0;
    int cnt;

    cp = buf;
    fgets(cp,sizeof(buf),xf);

    // scan first word
    for (chr = *cp++;  chr != 0;  chr = *cp++) {
        // stop on delimiter between first and second words
        if (chr == ' ')
            break;

        // convert char to histogram index
        chr -= 'a';

        // increment the histogram cell
        ++histo[chr];
    }

    // scan second word
    for (chr = *cp++;  chr != 0;  chr = *cp++) {
        // stop on end-of-line
        if (chr == '\n')
            break;

        // convert char to histogram index
        chr -= 'a';

        // decrement the histogram cell
        cnt = --histo[chr];

        // however, if we go negative, the second word has too many instances
        // of this char to match the first word
        if (cnt < 0) {
            fail = 1;
            break;
        }
    }

    do {
        // too many letters in second word that are _not_ in the first word
        if (fail)
            break;

        // scan histogram
        for (int idx = 0;  idx < 26;  ++idx) {
            if (histo[idx]) {
                fail = 1;
                break;
            }
        }
    } while (0);

    if (fail)
        printf("NO\n");
    else
        printf("YES\n");
}

// main -- main program
int
main(int argc,char **argv)
{
    char *file;
    FILE *xf;
    char buf[100];

    --argc;
    ++argv;

    file = *argv;
    if (file != NULL)
        xf = fopen(file,"r");
    else
        xf = stdin;

    fgets(buf,sizeof(buf),xf);
    int tstcnt = atoi(buf);

    for (int tstno = 1;  tstno <= tstcnt;  ++tstno)
        dotest(xf);

    if (file != NULL)
        fclose(xf);

    return 0;
}

答案 1 :(得分:0)

public static final int ASC = 97;

static boolean isAnagram(String a, String b) {
    boolean res = false;
    int len = a.length();
    if (len != b.length()) {
        return res;
    }
    a = a.toLowerCase();
    b = b.toLowerCase();
    int[] a_ascii = new int[26];
    int aval = 0;
    for (int i = 0; i < 2 * len; i++) {
        if (i < len) {
            aval = a.charAt(i) - ASC;
            a_ascii[aval] = (a_ascii[aval] == 0) ? (aval * len + 1) : (a_ascii[aval] + 1);
        } else {
            aval = b.charAt(i - len) - ASC;
            if (a_ascii[aval] == 0) {
                return false;
            }
            a_ascii[aval] = a_ascii[aval] - 1;
            res = (a_ascii[aval] == aval * len) ? true : false;
        }
    }
    return res;
}