【问题标题】:Swap letters in a string交换字符串中的字母
【发布时间】:2010-07-15 09:46:33
【问题描述】:

我需要使用以下规则交换字符串中的字母:

  • A 被 T 替换
  • T 被 A 替换
  • C 被 G 替换
  • G 被 C 替换

例如:ACGTA 应该变成 TGCAT

解决这个问题的最佳方法是什么?

【问题讨论】:

  • Replace in Array的可能重复
  • 有更紧凑的 DNA 字符串表示形式,每个碱基仅使用 2 位。如果这是一个严肃的研究项目,您可能需要考虑这些选项,而不是每个碱基使用 16 位 char(尤其是如果您的 DNA 字符串可以有数百万个碱基甚至更多)。
  • 我认为 real DNA 解密也有其他字母,用于表示特定类型的不确定性。我记得当我发现的时候非常惊讶,但是当你考虑到他们是如何进行测序时,这并没有那么令人吃惊。现在 AIUI 才刚刚开始让适当的数字等效音序器上线。
  • 可以使用查找表,考虑到 DNA 的小字母表,即使有 @DonalFellows 指出的模糊代码。这个问题已经过去了五年,但我在下面添加了一个更好的答案。

标签: java


【解决方案1】:

搜索java "A to T, T to A" 找到了这个suggestion:

String sequence = "AATTTCTCGGTTTCAAT";
sequence = sequence.replace("A", "t")
                   .replace("T", "a")
                   .replace("C", "g")
                   .replace("G", "c")
                   .toUpperCase();
System.out.println(sequence);

这是一个简单而简洁的解决方案,适用于您的特定情况,并且如果您的 DNA 字符串相对较短,它的性能也可以接受。对于处理大量数据的更通用的解决方案,您应该逐个迭代字符并构建一个新字符串。或者正如 polygenelubricants 指出的那样 - 考虑一种存储格式,每个碱基只使用 2 位而不是 16 位。

【讨论】:

  • 为什么是小写替换,然后是大写?
  • @fielding:避免两次替换同一个字符(A -> T -> A)。
  • 请注意,这种简单的方法会多次遍历字符串。单次迭代是可能的,并且可以提高速度。一次迭代还可以消除丢失原始字母大小写的需要,这有时用于传达信息。
【解决方案2】:

我会选择这样一个更通用的解决方案:

public String tr(String original, String trFrom, String trTo) {
  StringBuilder sb = new StringBuilder();

  for (int i = 0; i < original.length(); ++i) {
    int charIndex = trFrom.indexOf(original.charAt(i));
    if (charIndex >= 0) {
      sb.append(trTo.charAt(charIndex));
    } else {
      sb.append(original.charAt(i));
    }
  }

  return sb.toString(); 
}

像这样调用函数会得到你需要的结果:

tr("ACGTA", "ATCG", "TAGC")

所以功能和 unix tr 工具差不多:

echo ACGTA | tr ATCG TAGC

【讨论】:

    【解决方案3】:

    就像我explained yesterday,字符串是不可变的,你不能改变一个字符串,你必须创建一个新的并替换旧的。

    你可以这样解决你的问题:

    String s = "ACGTA";
    StringBuilder sb= new StringBuilder();
    for (char c:s.toCharArray()) {
      switch(c) {
        case 'A': sb.append('T');break;
        case 'T': sb.append('A');break;
        case 'C': sb.append('G');break;
        case 'G': sb.append('C');break;
        default: //handle error here -> invalid char in String
      }
    }
    s = sb.toString();
    

    此解决方案的优点是您不会创建太多 String 对象(每个“替换”操作都会创建一个新 String,如果您必须还原 很多 dna 序列)


    这是基于来自 polygenelubricants 和 rsp 的非常有用的 cmets 的更高性能版本:

    String s = "ACGTA";
    char[] reverse = new char[s.length()];
    for (int i = 0; i < reverse.length; i++) {
      switch(s.charAt(i)) {
        case 'A': reverse[i] = 'T';break;
        case 'T': reverse[i] = 'A';break;
        case 'C': reverse[i] = 'G';break;
        case 'G': reverse[i] = 'C';break;
        default: //handle error here -> invalid char in String
      }
    }
    s = new String(reverse);
    

    【讨论】:

    • -1:但是您可以将新字符串(即对旧字符串应用转换的结果)分配给包含旧字符串的变量。不要太迟钝。
    • 好的,谢谢,您昨天的解决方案帮助了我,但我在全部替换时遇到了问题,但现在可以使用 Mark Byers 解决方案
    • @Donal - 希望您在投反对票之前至少查看过引用的问题和答案。这个答案的核心是解决替换问题。昨天mac135好像重点改了原来的String,有种感觉,又要开始了。
    • +1;但请记住,DNA 字符串可能非常长(数百万个char 或更多),因此虽然.toCharArray() 上的for-each 在大多数情况下是最易读和可接受的,但在这种情况下可能不是(因为它空间需求翻倍)。
    • 如果数据在String,所有操作都会增加空间需求。如果他们在StringBuilder 中,那么您只有一组有限的操作可用。由于只执行 char-for-char 替换,真正的解决方案是将所有内容保存在 char 数组中并手动进行处理(嗯,在循环内切换),这样您就永远不会复制任何内容或有任何愚蠢的开销。
    【解决方案4】:

    DNA 有一个小字母表。您可以使用lookup table,将一些语句替换为简单的数组索引。

    这种方法:

    • 仅遍历序列一次。
    • 消除条件语句。
    • 可以在字母大小写方面保持稳定,有时用于在 DNA 序列中传达信息。
    • 可以处理 IUPAC 歧义代码。
    • 可以处理间隙。
    • 可以很容易地提供一个反向补码。

    首先,您需要一个查找表。

    private static final String COMPLEMENT_TABLE 
      // 0123456789ABCDEF0123456789ABCDEF
      = "                                " // 0-31
      + "             -                  " // 32-63
      + " TVGH  CD  M KN   YSAABWXR      " // 64-95
      + " tvgh  cd  m kn   ysaabwxr      "; // 96-127
      //  ABCDEFGHIJKLMNOPQRSTUVWXYZ
    
    private static final byte[] COMPLEMENT_TABLE_BYTES 
      = COMPLEMENT_TABLE.getBytes( StandardCharsets.US_ASCII );
    

    然后,您可以通过简单的查表找到补码的碱基。

    public static byte[] complement( byte[] sequence ) {
        int length = sequence.length;
        byte[] result = new byte[ length ];
    
        for ( int i = 0; i < length; ++i ) {
            result[i] = COMPLEMENT_TABLE_BYTES[ sequence[i] ];
        }
    
        return result;
    }
    

    如果需要方便使用小序列,您可以提供一个接受并返回字符串的方法。

    public static String complement( String sequence ) {
        byte[] complementBytes = complement( 
          sequence.getBytes( StandardCharsets.US_ASCII ));
        return new String( complementBytes, StandardCharsets.US_ASCII );
    }
    

    可以在同一个循环中计算反向补码。

    public static byte[] reverseComplement( byte[] sequence ) {
        int length = sequence.length;
        byte[] result = new byte[ length ];
    
        for ( int i = 0; i < length; ++i ) {
            result[ (length - i) - 1] = COMPLEMENT_TABLE_BYTES[ sequence[i] ];
        }
    
        return result;
    }
    
    public static String reverseComplement( String sequence ) {
        byte[] complementBytes = reverseComplement( 
          sequence.getBytes( StandardCharsets.US_ASCII ));
        return new String( complementBytes, StandardCharsets.US_ASCII );
    }
    

    使用您的示例序列:

    public static void main(String[] args) {
        String sequence = "ACGTA";
    
        String complementSequence = complement( sequence );
        System.out.println( String.format( 
           "complement(%s) = %s", sequence, complementSequence ));
    
        String reverseComplementSequence = reverseComplement( sequence );
        System.out.println( String.format( 
          "reverseComplement(%s) = %s", sequence, reverseComplementSequence ));
    }
    

    我们得到这个输出:

    complement(ACGTA) = TGCAT
    reverseComplement(ACGTA) = TACGT
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-31
      • 1970-01-01
      • 2019-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多