【问题标题】:Check anagrams using sql server使用 sql server 检查字谜
【发布时间】:2016-08-07 17:15:22
【问题描述】:

ACTCAT 是字谜

我必须在 sql server 中编写一个函数,该函数接受 2 个字符串并给出一个布尔输出,指示它们是否都是字谜。

在sql server中这样做没有意义,只是为了学习目的

【问题讨论】:

  • 这个网站不是用来做作业的。请至少发布您自己已经做过的尝试。
  • 两个字符串的开始长度应该相同。如果他们这样做,您将不得不将字符串拆分为单独的字母。可能最好将字母放在表格中。然后你必须检查它们有相同的字母,每个字母的数字相同。

标签: sql sql-server database


【解决方案1】:

SQL Server 不擅长这种事情,但是你在这里:

WITH Src AS
(
    SELECT * FROM (VALUES
    ('CAT', 'ACT'),
    ('CAR', 'RAC'),
    ('BUZ', 'BUS'),
    ('FUZZY', 'MUZZY'),
    ('PACK', 'PACKS'),
    ('AA', 'AA'),
    ('ABCDEFG', 'GFEDCBA')) T(W1, W2)
), Numbered AS
(
    SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) Num
    FROM Src
), Splitted AS
(
    SELECT Num, W1 Word1, W2 Word2, LEFT(W1, 1) L1, LEFT(W2, 1) L2, SUBSTRING(W1, 2, LEN(W1)) W1, SUBSTRING(W2, 2, LEN(W2)) W2
    FROM Numbered
    UNION ALL
    SELECT Num, Word1, Word2, LEFT(W1, 1) L1, LEFT(W2, 1) L2, SUBSTRING(W1, 2, LEN(W1)) W1, SUBSTRING(W2, 2, LEN(W2)) W2
    FROM Splitted
    WHERE LEN(W1)>0 AND LEN(W2)>0
), SplitOrdered AS
(
    SELECT *,
        ROW_NUMBER() OVER (PARTITION BY Num ORDER BY L1) LNum1,
        ROW_NUMBER() OVER (PARTITION BY Num ORDER BY L2) LNum2
    FROM Splitted
)
SELECT S1.Num, S1.Word1, S1.Word2, CASE WHEN COUNT(*)=LEN(S1.Word1) AND COUNT(*)=LEN(S1.Word2) THEN 1 ELSE 0 END Test
FROM SplitOrdered S1
JOIN SplitOrdered S2 ON S1.L1=S2.L2 AND S1.Num=S2.Num AND S1.LNum1=S2.LNum2
GROUP BY S1.Num, S1.Word1, S1.Word2

结果:

1   CAT     ACT     1
2   CAR     RAC     1
3   BUZ     BUS     0
4   FUZZY   MUZZY   0
5   PACK    PACKS   0
6   AA      AA      1
7   ABCDEFG GFEDCBA 1

【讨论】:

  • 哇,非常好的查询!试过了,效果很好,也有重复的字符(见我的其他评论)。顺便说一句,也学到了一些新东西,不知道select 中的values 子句。
  • @Tanner 是的,但这也是一个问答网站,可以让其他用户受益。从这个角度来看,这是一个完美的查询,可以通过复制粘贴使用,您也可以通过分析它受益,因此,它应该upvoted!
  • 很抱歉,虽然这是一个很酷的 CTE 用法,但此查询运行速度相当慢(至少在我的服务器上)。 160 000 行在 24 秒内。我创建了一个带有循环的函数,它在 0.something 秒内完成了这项工作。也许其他人也可以测试/确认这一点?
【解决方案2】:

首先将两个词拆分 (T-SQL Split Word into characters) 到临时表中。然后执行外连接并检查空值。

感谢乔治的评论编辑:

  1. 将 (T-SQL Split Word into characters) 两个单词拆分为临时表
  2. 修改临时表或使用 CTE 添加带有 count(*) 和 group by letters 子句的列
  3. 使用字母对两个临时表执行完全外连接,并在连接条件中计数
  4. 检查输出中的空值 - 如果没有,则说明您有一个字谜

【讨论】:

  • 小心重复字符。例如,ABCCABBC 这两个词可能会显示为字谜,而实际上不会。
  • 是的。另一列存储两个表中每个字母的计数和连接条件应该修复它。
【解决方案3】:

我想到的第一个:

DECLARE @word1 nvarchar(max) = NULL,
        @word2 nvarchar(max) = 'Test 1',
        @i int = 0, @n int

DECLARE @table TABLE (
    id int,
    letter int
)

SELECT @word1 = ISNULL(LOWER(@word1),''), @word2 = ISNULL(LOWER(@word2),'')

SELECT @n = CASE WHEN LEN(@word1) > LEN(@word2) THEN LEN(@word1) ELSE LEN(@word2) END

WHILE @n > 0
BEGIN
    INSERT INTO @table
    SELECT 1, ASCII(SUBSTRING(@word1,@n,1))
    UNION ALL
    SELECT 2, ASCII(SUBSTRING(@word2,@n,1))
    SET @n=@n-1
END

SELECT CASE WHEN COUNT(*) = 0 THEN 1 ELSE 0 END isAnagram
FROM (
    SELECT id, letter, COUNT(letter) as c
    FROM @table
    WHERE id = 1
    GROUP BY id, letter)as t
FULL OUTER JOIN (
    SELECT id, letter, COUNT(letter) as c
    FROM @table
    WHERE id = 2
    GROUP BY id, letter) as p
    ON  t.letter = p.letter and t.c =p.c
WHERE t.letter is NULL OR p.letter is null

输出:

isAnagram
0

【讨论】:

  • 啊,添加计数来对抗重复的字母:)
  • 感谢您的检查 :) 我用了一些真实的词,而且还可以(当然)
  • 检查“Test1”与“NULL”和“Test1Test2”与“Test1 Test2”,它们将返回“1”。
  • @JarleBjørnbeth 谢谢,我听到了你的声音并添加了一些检查:) 现在如果有人感兴趣,它对小写/大写不敏感!
  • @gofr1 嘿嘿,手感不错 :)
【解决方案4】:

您还可以在函数中使用循环,它们可以快速运行。即使接近此功能的性能,我也无法获得任何其他答案:

CREATE FUNCTION IsAnagram 
(
    @value1 VARCHAR(255)
    , @value2 VARCHAR(255)
) 
RETURNS BIT
BEGIN

    IF(LEN(@value1) != LEN(@value2))
        RETURN 0;

    DECLARE @firstChar VARCHAR(3);

    WHILE (LEN(@value1) > 0)
    BEGIN
        SET @firstChar = CONCAT('%', LEFT(@value1, 1), '%');

        IF(PATINDEX(@firstChar, @value2) > 0)
            SET @value2 = STUFF(@value2, PATINDEX(@firstChar, @value2), 1, '');
        ELSE
            RETURN 0;

        SET @value1 = STUFF(@value1, 1, 1, '');

    END

    RETURN (SELECT IIF(@value2 = '', 1, 0));

END

GO

SELECT dbo.IsAnagram('asd', 'asd')
--1
SELECT dbo.IsAnagram('asd', 'dsa')
--1
SELECT dbo.IsAnagram('assd', 'dsa')
--0
SELECT dbo.IsAnagram('asd', 'dssa')
--0
SELECT dbo.IsAnagram('asd', 'asd')

【讨论】:

    【解决方案5】:

    这是一个数字表可以提供帮助的东西。

    创建和填充小数字表的代码如下。

    CREATE TABLE dbo.Numbers
      (
         Number INT PRIMARY KEY
      );
    
    WITH Ten(N) AS 
    (
        SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
        SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
        SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
    )   
    INSERT INTO dbo.Numbers
    SELECT ROW_NUMBER() OVER (ORDER BY @@SPID) AS Number
    FROM   Ten T10,
           Ten T100,
           Ten T1000
    

    设置好之后就可以使用了

    SELECT W1,
           W2,
           IsAnagram = CASE
                         WHEN LEN(W1) <> LEN(W2)
                           THEN 0
                         ELSE
                           CASE
                             WHEN EXISTS (SELECT SUBSTRING(W1, Number, 1),
                                                 COUNT(*)
                                          FROM   dbo.Numbers
                                          WHERE  Number <= LEN(W1)
                                          GROUP  BY SUBSTRING(W1, Number, 1)
                                          EXCEPT
                                          SELECT SUBSTRING(W2, Number, 1),
                                                 COUNT(*)
                                          FROM   dbo.Numbers
                                          WHERE  Number <= LEN(W2)
                                          GROUP  BY SUBSTRING(W2, Number, 1))
                               THEN 0
                             ELSE 1
                           END
                       END 
    FROM  (VALUES
            ('CAT', 'ACT'),
            ('CAR', 'RAC'),
            ('BUZ', 'BUS'),
            ('FUZZY', 'MUZZY'),
            ('PACK', 'PACKS'),
            ('AA', 'AA'),
            ('ABCDEFG', 'GFEDCBA')) T(W1, W2)
    

    或者替代实现可以是

       IsAnagram = CASE
                     WHEN LEN(W1) <> LEN(W2)
                       THEN 0
                     ELSE
                       CASE
                         WHEN EXISTS (SELECT 1
                                      FROM   dbo.Numbers N
                                             CROSS APPLY (VALUES(1,W1),
                                                                (2,W2)) V(Col, String)
                                      WHERE  N.Number <= LEN(W1)
                                      GROUP  BY SUBSTRING(String, Number, 1)
                                      HAVING COUNT(CASE WHEN Col = 1 THEN 1 END) <> 
                                             COUNT(CASE WHEN Col = 2 THEN 1 END))
                           THEN 0
                         ELSE 1
                       END
                   END 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-15
      • 1970-01-01
      • 2013-08-18
      • 2021-10-10
      • 2021-12-12
      相关资源
      最近更新 更多