hi guy.
这个问题如果单纯对比原始的串和最终串的话,没有任何意义,举个简单的例子,我有字串a="abcd",把"ab"删除,剩下"cd"。用上边的方法一个都不可行。再比如把"cd"插入到a串最前得到"cdabcd",这时候应该把前边的"cd"设置高亮,按照前边的方法也无法做到。所以我认为需要记录操作步骤来做。而“增删改”其实就只是“增”和“删”,其中“改”可以通过前两个操作来体现。那么就实现记录“增”和“删”的操作就行了。
例如函数:add(string target,int idx,string str) //在指定串target的指定位置插入串str
del(string target,int idx,int len) //删除指定串target中从idx开始,长度为len的子串
通过这样的定义可以完整保留操作信息,例如有串a='abcd'。对a删除'bcd',这时候调用
del(a,1,3)
而在del函数内部可以记录下串target的操作是'delete',从索引 1 开始,长度为 3。这样子在显示的时候,只要把串a中从1开始长度为3的一部分设置高亮即可。再比如在串a中插入一个一样的串"abcd",得到b="abcdabcd",这个时候按照上边的办法无法区分哪个是新的。而调用函数
add(a,0,"abcd")
这样子就可以设置最终串b中从索引0开始,长度为4的子串高亮。
我想这样足以解决任何问题,多个操作的时候也可以实现。希望对楼主有帮助。:)
非常感谢你,首先我只能得到2个字符串,其次这个功能使我们公司的一个管理系统中的小功能,管理需要对比2个字符串,字符串是一句话,可能是英文、法文、俄语、西班牙语、葡萄牙语,如果是删除的部分我们理想的效果是把删除的补充出来并特殊显示,增加的就高亮显示,功能很类似SVN的Diff功能只不过SVN是逐行比较,我们需要逐个单词比较,从目前的情况来看,这个功能不怎么好实现,主要是没有算法,如果谁有好的想法希望告知下。
@meng5619: 如果不能得到操作步骤,仅仅得到最后的结果,最后作一下对比,可能反映不了真实情况,但还是能够标识出区别的。但必须要有约定,比如a="abc",b="aabc",这个情况下可以认为是在a字符串的最前边插入了'a',也可以认为是字符'b'之前插入了'a'。我们就约定a中的一个字符在b中第一次出现的位置是原来的字符串,以后出现更多的是插入的。因此上面应该是认为在字符'b'之前插入了'a'。再例如a="xyzkln",b="xklnyz",这个时候可以认为是"yz"被删除并插入到末尾,也可以认为"kln"被删除并插入到"x"的后面。这里就约定a串中的"x"与"y"原先没有字符串,那b串中它们之间的字符串是被插入的,而后面的"kln"被删除了。按照这两个约定,我尝试用javascript来写下算法代码。
原理:用两个队列维护添加和删除的记录分别为addIdx,delIdx。addIdx结构为start和end两个属性。delIdx结构为idx和word两属性。设置两个指针,分别从a,b两个字符串的索引0开始,对于a中的每一个字符(或者单词),在b中找第一次出现的索引,如果能找到,并且相对距离未改变,则两个指针同时前进,若相对距离改变了则之间的串为插入串,放到addIdx中,例如上面的"kln",如果找不到,则a串中的当前字符在b串中被删除。记录删除的字符和索引。
其实这个比较难说清楚,要有图就很容易看清。下面是javascript代码:
1 function diffStr(a,b){ 2 var i,j,k,l; 3 for(i=j=0;i<a.length&&j<b.length;i++){ 4 k=j; 5 while(b.charAt(k)!=a.charAt(i) && k<b.length){ 6 k++; 7 } 8 if(k<b.length){ 9 if(j!=k) addIdx.push({start:j,end:k-1}); 10 j=k+1; 11 } 12 else{ 13 delIdx.push({idx:j,word:a.charAt(i)}); 14 } 15 } 16 if(j<b.length){ 17 addIdx.push({start:j,end:b.length-1}); 18 } 19 while(i<a.length){ 20 delIdx.push({idx:i}); 21 i++; 22 } 23 }
这个函数的作用就是通过上边描述的算法产生addIdx和delIdx。这样所有信息都有了。之后的事情便是打印出来,我这边写了一个插入的高亮代码,删除的原理也一样,楼主可以自己实现。
1 function recOut(str,offset){ 2 if(n=addIdx.shift()){ 3 return str.slice(0,n.start-offset).concat('<span class=\'highlight\'>'+str.slice(n.start-offset,n.end-offset+1)+'</span>').concat(recOut(str.slice(n.end-offset+1),n.end+1)); 4 } 5 else 6 return str; 7 }
其实就是不断出队列,再递归直到队空。下面是测试和效果:
1 var a="不以结婚为目的的谈恋爱都是耍流氓"; 2 var b='毛主席说:\'不以为目的谈恋人都是耍流氓流氓\''; 3 addIdx=[]; 4 delIdx=[]; 5 diffStr(a,b);//算法主要部分 6 var finalStr = recOut(b,0); 7 document.write(finalStr);
这时候的addIdx值:[{start:0,end:5},{start:13,end:13},{start:19,end:21}],
delIdx的值:[{idx:8,word:"结"},{idx:8,word:"婚"},{idx:11,word:"的"},{idx:13,word:"爱"}]。
结果:
毛主席说:'不以为目的谈恋人都是耍流氓流氓'
应该说效果全都有了,再加上删除的就完美了。我晕,写了这么多,可以写一篇文章了,有时间整理一下。另外楼主说的多语言问题也不存在,只要划分好粒度即可,例如我这是中文,你对日文也一样的,英文的话更方便啦。要是有问题再回复我吧,写了这么多希望能帮上忙:)
@月窟仙人: 非常感谢你,这几天忙别的事了,先说声抱歉,你的方法我还没有做相应的测试,我这有一个算法,虽然有些不完美,不过已经满足绝大部分情况了,和大家分享下:
public static string GetCompare(string stringNameA, string stringNameB)
{
if (string.IsNullOrEmpty(stringNameA))
{
return " <strong class="Highlight">" + stringNameB + "</strong>";
}
string[] stringNameAS = stringNameA.Split(' ');
string[] stringNameBS = stringNameB.Split(' ');
int a = stringNameAS.Length;
int b = stringNameBS.Length;
int count = a >= b ? b : a;
string stringNameC = "";
int m = 0;
int n = 0;
for (int i = 0; i < b; i++)
{
if (m >= b)
{
break;
}
if (n == a)
{
n = a - 1;
}
if (n > a)
{
for (int x = n; x < b; x++)
{
stringNameC = stringNameC + " <strong class="Highlight">" + stringNameBS[x] + "</strong>";
}
break;
}
if (stringNameAS[n] == stringNameBS[m])
{
stringNameC = stringNameC + " " + stringNameBS[m];
}
else
{
for (int k = m; k < b; k++)
{
if (stringNameBS[k] == stringNameAS[n])
{
int z = 0;
for (int y = 0; y < k; y++)
{
if (stringNameBS[y] == stringNameBS[k])
{
z = 1;
break;
}
}
if (z == 0)
{
stringNameC = stringNameC + " " + stringNameBS[k];
}
else
{
stringNameC = stringNameC + " <strong class="Highlight">" + stringNameBS[k] + "</strong>";
}
m = k;
break;
}
else
{
int g = 0;
for (int j = n; j < a; j++)
{
if (stringNameBS[k] == stringNameAS[j])
{
n = j;
m = k;
g = 1;
break;
}
}
if (g == 1)
{
stringNameC = stringNameC + " " + stringNameBS[k];
break;
}
else
{
stringNameC = stringNameC + " <strong class="Highlight">" + stringNameBS[k] + "</strong>";
}
}
}
}
m++;
n++;
}
return stringNameC.Trim();
}
这段代码是别人给我的,其中的原理写字真的很难说清楚,可能是我语言表达有问题,哈。其实我们这还有一种算法,和二分法有些类似,不过代码实现有点困难,反正我写不出来,如果谁擅长写这种逻辑代码可+我QQ781238222。好了,感谢大家。多多交流。园豆给你,希望能多多帮助新人。
临时变量 try { using(事务上下文(可以自己实现事务资源管理器)) { 修改对象A 转变 使其转换为B的特征 用变量记录下来修改的部分为哪些 事务提交 记录下来的变量就为修改的(高亮显示) } } catch (Exception) { 事务自动回滚(变量还变回原来的)不会高亮 } finally { }
string A = "博客园"; string B = "第一个位置加1博客第二位置园"; IEnumerable<char> cc = B.Except(A); string C = string.Join("", B.Select(s => cc.Contains(s) ? string.Format("<font style='color:{1}'>{0}</font>", s, "red") : s.ToString()));
非常感谢你,我测试了下,目前发现一种情况不能满足我的情况,如A=“ABCD” ,B="ABCDD",即添加一个单词时,这个单词不能和A中重复,否则无法找出。
@meng5619:
那你 for循环遍历 一下
string A = "ABCD"; string B = "ABCDD"; List<int> Change = new List<int>(); for (int i = 0; i < B.Length; i++) { if (A.Length > i) { if (A[i] != B[i]) { Change.Add(i); } } else { Change.Add(i); } } string C = string.Join("", B.Select((s, i) => Change.Contains(i) ? string.Format("<font style='color:{1}'>{0}</font>", s, "red") : s.ToString()));