Z 函数(扩展KMP)

author: LeoJacob, Marcythm, minghu6

约定:字符串下标以 \(0\) 为起点。

定义

对于一个长度为 \(n\) 的字符串 \(s\),定义函数 \(z[i]\) 表示 \(s\)\(s[i,n-1]\)(即以 \(s[i]\) 开头的后缀)的最长公共前缀(LCP)的长度,则 \(z\) 被称为 \(s\)Z 函数。特别地,\(z[0] = 0\)

国外一般将计算该数组的算法称为 Z Algorithm,而国内则称其为 扩展 KMP

这篇文章介绍在 \(O(n)\) 时间复杂度内计算 Z 函数的算法以及其各种应用。

解释

下面若干样例展示了对于不同字符串的 Z 函数:

  • \(z(\mathtt{aaaaa}) = [0, 4, 3, 2, 1]\)
  • \(z(\mathtt{aaabaab}) = [0, 2, 1, 0, 2, 1, 0]\)
  • \(z(\mathtt{abacaba}) = [0, 0, 1, 0, 3, 0, 1]\)

朴素算法

Z 函数的朴素算法复杂度为 \(O(n^2)\)
C++

vector<int> z_function_trivial(string s) {
	int n = (int)s.length();
	vector<int> z(n);
	for (int i = 1; i < n; ++i)
		while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
	return z;
}

Python

def z_function_trivial(s):
n = len(s)
z = [0] * n
for i in range(1, n):
	while i + z[i] < n and s[z[i]] == s[i + z[i]]:
		z[i] += 1
		return z

线性算法

如同大多数字符串主题所介绍的算法,其关键在于,运用自动机的思想寻找限制条件下的状态转移函数,使得可以借助之前的状态来加速计算新的状态。

在该算法中,我们从 \(1\)\(n-1\) 顺次计算 \(z[i]\) 的值(\(z[0]=0\))。在计算 \(z[i]\) 的过程中,我们会利用已经计算好的 \(z[0],\ldots,z[i-1]\)

对于 \(i\),我们称区间 \([i,i+z[i]-1]\)\(i\)匹配段,也可以叫 Z-box。

算法的过程中我们维护右端点最靠右的匹配段。为了方便,记作 \([l,r]\)。根据定义,\(s[l,r]\)\(s\) 的前缀。在计算 \(z[i]\) 时我们保证 \(l\le i\)。初始时 \(l=r=0\)

在计算 \(z[i]\) 的过程中:

  • 如果 \(i\le r\),那么根据 \([l,r]\) 的定义有 \(s[i,r] = s[i-l,r-l]\),因此 \(z[i]\ge \min(z[i-l],r-i+1)\)。这时:
  • \(z[i-l] < r-i+1\),则 \(z[i] = z[i-l]\)
  • 否则 \(z[i-l]\ge r-i+1\),这时我们令 \(z[i] = r-i+1\),然后暴力枚举下一个字符扩展 \(z[i]\) 直到不能扩展为止。
  • 如果 \(i>r\),那么我们直接按照朴素算法,从 \(s[i]\) 开始比较,暴力求出 \(z[i]\)
  • 在求出 \(z[i]\) 后,如果 \(i+z[i]-1>r\),我们就需要更新 \([l,r]\),即令 \(l=i, r=i+z[i]-1\)

可以访问 这个网站 或者 后面的这个代码 来看 Z 函数的模拟过程。
C++

	vector<int> z_function(string s) {
		int n = (int)s.length();
		vector<int> z(n);
		for (int i = 1, l = 0, r = 0; i < n; ++i) {
			if (i <= r && z[i - l] < r - i + 1) {
				z[i] = z[i - l];
			} else {
				z[i] = max(0, r - i + 1);
				while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
			}
			if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
		}
		return z;
	}

Python

def z_function(s):
n = len(s)
z = [0] * n
l, r = 0, 0
for i in range(1, n):
	if i <= r and z[i - l] < r - i + 1:
		z[i] = z[i - l]
		else:
			z[i] = max(0, r - i + 1)
			while i + z[i] < n and s[z[i]] == s[i + z[i]]:
				z[i] += 1
				if i + z[i] - 1 > r:
					l = i
					r = i + z[i] - 1
					return z

复杂度分析

对于内层 while 循环,每次执行都会使得 \(r\) 向后移至少 \(1\) 位,而 \(r< n-1\),所以总共只会执行 \(n\) 次。

对于外层循环,只有一遍线性遍历。

总复杂度为 \(O(n)\)

应用

我们现在来考虑在若干具体情况下 Z 函数的应用。

这些应用在很大程度上同 前缀函数 的应用类似。

匹配所有子串

为了避免混淆,我们将 \(t\) 称作 文本,将 \(p\) 称作 模式。所给出的问题是:寻找在文本 \(t\) 中模式 \(p\) 的所有出现(occurrence)。

为了解决该问题,我们构造一个新的字符串 \(s = p + \diamond + t\),也即我们将 \(p\)\(t\) 连接在一起,但是在中间放置了一个分割字符 \(\diamond\)(我们将如此选取 \(\diamond\) 使得其必定不出现在 \(p\)\(t\) 中)。

首先计算 \(s\) 的 Z 函数。接下来,对于在区间 \([0,|t| - 1]\) 中的任意 \(i\),我们考虑以 \(t[i]\) 为开头的后缀在 \(s\) 中的 Z 函数值 \(k = z[i + |p| + 1]\)。如果 \(k = |p|\),那么我们知道有一个 \(p\) 的出现位于 \(t\) 的第 \(i\) 个位置,否则没有 \(p\) 的出现位于 \(t\) 的第 \(i\) 个位置。

其时间复杂度(同时也是其空间复杂度)为 \(O(|t| + |p|)\)

本质不同子串数

给定一个长度为 \(n\) 的字符串 \(s\),计算 \(s\) 的本质不同子串的数目。

考虑计算增量,即在知道当前 \(s\) 的本质不同子串数的情况下,计算出在 \(s\) 末尾添加一个字符后的本质不同子串数。

\(k\) 为当前 \(s\) 的本质不同子串数。我们添加一个新的字符 \(c\)\(s\) 的末尾。显然,会出现一些以 \(c\) 结尾的新的子串(以 \(c\) 结尾且之前未出现过的子串)。

设串 \(t\)\(s + c\) 的反串(反串指将原字符串的字符倒序排列形成的字符串)。我们的任务是计算有多少 \(t\) 的前缀未在 \(t\) 的其他地方出现。考虑计算 \(t\) 的 Z 函数并找到其最大值 \(z_{\max}\)。则 \(t\) 的长度小于等于 \(z_{\max}\) 的前缀的反串在 \(s\) 中是已经出现过的以 \(c\) 结尾的子串。

所以,将字符 \(c\) 添加至 \(s\) 后新出现的子串数目为 \(|t| - z_{\max}\)

算法时间复杂度为 \(O(n^2)\)

值得注意的是,我们可以用同样的方法在 \(O(n)\) 时间内,重新计算在端点处添加一个字符或者删除一个字符(从尾或者头)后的本质不同子串数目。

字符串整周期

给定一个长度为 \(n\) 的字符串 \(s\),找到其最短的整周期,即寻找一个最短的字符串 \(t\),使得 \(s\) 可以被若干个 \(t\) 拼接而成的字符串表示。

考虑计算 \(s\) 的 Z 函数,则其整周期的长度为最小的 \(n\) 的因数 \(i\),满足 \(i+z[i]=n\)

该事实的证明同应用 前缀函数 的证明一样。

练习题目


本页面主要译自博文 Z-функция строки и её вычисление 与其英文翻译版 Z-function and its calculation。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。

TAG

Z 函数(扩展KMP)

模拟程序

<!-- Hello -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252"><style>body {transition: opacity ease-in 0.2s; } 
body[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } 
</style><script src="chrome-extension://memhacajcfhmibggbgilihlmiiddeggo/assets/main-world.ts-4ed993c7.js" type="module"></script>
	<meta name="Author" content="John Yundt-Pacheco">
	<meta name="Keywords" content="Animation, Pattern Matching, Algorithms">
	<meta name="Description" content="Dr. Bereg, CS 6333 Algorithms for Computational Biology">
	<title>Z Algorithm (JavaScript Demo)</title>
	<!--link rel="stylesheet" type="text/css" href="/styles.css"-->

	<style>
		body { margin:0; }
		body, td { font-family:verdana; font-size:13px; }
		table.fullsize { margin:0; padding:0; width:100%; height:100%; }
		h1 { margin-top:0px; font-family:verdana; font-size:16px; font-style:italic; }
		h2 { margin-top:0px; font-family:verdana; font-size:14px; font-weight:bold; }
		div.divMenuItem a:hover, div#menuBar a:hover { color:moccasin; }
		div.divMenuItem { position:absolute; display:none; padding:5px 8px 5px 8px; background-color:#6699CC; }
		#menuTd, #menuTd a { text-decoration:none; color:white; font-size:10px; font-weight:bold; }
		div.fh-tabs { display:none; }
		TD.content { border-width:0px; font-family:verdana; font-size:12px; }
		TH.content { border-width:0px; font-family:verdana; font-size:90%; }
		TR.header2 { height:30px; background-color:lightskyblue; }
		TR.header3 { background-color:#D9E9F9; }
		TR.closing { height:20px; background-color:#E5E5E5; border-width:0px; }
		TR.list { background-color:#EEEEEE; border-width:0px; }
		TT.link { color:blue; text-decoration:underline; clear:none; }
		TT.blue { color:blue; font-weight:bold; font-size:12px; font-family:verdana; }
		TT.gold { color:gold; font-weight:bold; font-size:12px; font-family:verdana; }
		TT.hand { text-decoration:none; font-size:12px; font-family:verdana; }
	</style>
</head>

<body>

<table class="fullsize" cellpadding="5" cellspacing="0" border="0">

<tbody><tr height="*">
	<td colspan="3" valign="top" height="100%" style="padding:10px">
<!-- this is the main section -->

<script>
function ce(elName,parent,elId){
	var el = document.createElement(elName);
	if (elId) el.id = elId;
	if (parent) parent.appendChild(el);
	return el;
}

function ctn(text,parent){
	var tn = document.createTextNode(text);
	if (parent) parent.appendChild(tn);
	return tn;
}

function ge(id){
	return document.getElementById(id);
}

function removeAllChildren(el){
	while (el.firstChild)
		el.removeChild(el.firstChild);
}

function sprn(text,d){
    ctn(text,ce('p',d));  
}

function buildTable(t, el){
	var tbody = ce('tbody', table = ce('table', el));
	table.border = 1;
	table.style.border = 0;
	table.cellSpacing = 1;
    var i,j,td,o;
	for (i=0; i<t.length; i++){
	    tr = ce('tr', tbody);
		for (j=0; j<t[i].length; j++){
		    td = ce('td', tr);
			o = t[i][j];

			if (o.text != undefined){
				if (o.color) td.style.color = o.color;
				if (o.colSpan) td.colSpan = o.colSpan;
				if (o.align) td.align= o.align;
				if (o.width) td.style.width = o.width;
				if (o.bgColor) td.style.backgroundColor = o.bgColor;
				td.innerHTML = o.text;
			} else {
				ctn(o, td);
			}
		}
	}
}


var onkeypressListeners = new Array();
document.onkeypress = function(e){ for (var i=0; i<onkeypressListeners.length; i++) onkeypressListeners[i](e); }

function pw(s,w){ var ret = ''+s; while (ret.length<w) ret = ' '+ret; return ret; }
function div3(x){ return Math.floor(x/3); }
function leq1(a1,a2, b1,b2){ return a1 < b1 || (a1 == b1 && a2 <= b2); }
function leq2(a1,a2,a3, b1,b2,b3){ return a1 < b1 || (a1 == b1 && leq1(a2,a3, b2,b3)); }

function bld_table(text,n,colors,table){
    var i;
    for(i=0;i<n;i++){
        if(i< text.length)
            table.push({align:'center',width:25, text:text.charAt(i), bgColor:colors[i] });
       else
            table.push({align:'center',width:25, text:'', bgColor:'' });

    }
}
function animate(text){
	var div = ce('div');
	div.style.backgroundColor = '#DDDDCC';
	div.style.padding = 14;

    var match = false;
    var matches = 0;
	var nt = text.length;
    var table = new Array(); table[0] = new Array(); table[1] = new Array(); table[2] = new Array();
	table[3] = new Array(); 
    var tcolors = new Array(); pcolors = new Array(); zcolors = new Array();
    table[0].push({text:'Index:', align:'right'});
	table[1].push({text:'Text:', align:'right'});
    table[2].push({text:'Text:', align:'right'});
	table[3].push('Z values:');
    table[3].push('');
    var z = new Array(nt);
    var i,j,k,l;
    for(i=0;i<nt;i++){
        tcolors[i]='';
        pcolors[i]='';
		zcolors[i]='';
    }
    for(i=0;i<nt;i++){
		table[0].push({text:i, align:'center'});    
    }
    bld_table(text,nt,tcolors,table[1]);
    bld_table(text,nt,tcolors,table[2]);
	buildTable(table, div);
    sprn('To illustrate the comparisons taking place, the text has been duplicated. Green boxes indicate matches, red a mismatch.',div);
	sprn('In the Z algorithm, k is the index being considered, l is the left side of the Z-box, and r is the right side of the Z-box,',div);
	sprn('The current Z box (bounded by l and r) is indicated on the Index row using pale green.',div);
    sprn('The Z algorithm starts with k=1, r=0, l=0.',div);
    sprn('Press x to generate Z values for the text using the Z Algorithm.',div);

	snapshots.push(div.cloneNode(true));
    var r = 0, zk=0, si=0, kOld, zOld, b, zc ;
    var cmp = 0;
    for(k=1;k<nt;k++){
        for(i=0;i<nt;i++){
           tcolors[i]='';
           pcolors[i]='';
		   zcolors[i]='';
        }
		for(i=l;i<=r;i++){
			zcolors[i] = 'palegreen';
		}

        sprn('_______________________________________________________________________________________',div);
        sprn('Evaluating k='+k+'.',div);
        if(k>r){
            sprn('Current index (k='+k+') is past end of last Z-box (r='+r+'), so Z('+k+') must be computed explicitly.',div);
            zk = 0;
            for(si=0;si<nt;si++){
                cmp++;           
                if((si+k<nt)&&(text.charAt(si) == text.charAt(si+k))){
                    pcolors[si+k] = tcolors[si] = 'green';
                    continue;
                }else{
                    if(si+k<nt){
                      pcolors[si+k]=tcolors[si] = 'red';
                    }
                    break;
                }
            }
            if(si>0){
              zk = si
              r = zk + k - 1
              l = k          
            }
        }else{

             sprn('Current index (k='+k+') is in a previously discovered substring (r='+r+').',div);
             kOld = k - l
             zOld = z[kOld]
             b = r - k + 1
             if (zOld < b){
                zk = zOld
                sprn('The previously discovered substring starts at k-l ('+k+'-'+l+'), with Z('+(k-l)+')='+zk+' which is < the remaining substring S['+k+'..'+r+'], so Z('+k+')=Z('+(k-l)+')='+zk+'.',div);
                sprn('No additional comparisons needed.',div);
                for(var zc=-0;zc<zk;zc++){
                   pcolors[zc+k] = tcolors[zc] = 'green';
                }
                
             }else{
             sprn('The previously discovered substring starts at k-l ('+k+'-'+l+'), with Z('+(k-l)+')='+zk+' which is not < the remaining substring S['+k+'..'+r+'], so additional comparisons are needed.',div);
                zk = b
                for(zc=0;zc<zk;zc++){
                   pcolors[zc+k] = tcolors[zc] = 'green';
                }

                for(si=b;si<nt;si++){
                    cmp++;
                    if((k + si < nt) &&(text.charAt(si) == text.charAt(k+si))){
                        pcolors[si+k] = tcolors[si] = 'green';
                        continue;
                    }else{
                       if(si+k<nt){
                          pcolors[si+k]=tcolors[si] = 'red';
                       }   
                       break;                  
                    }   
                }
                zk = si;
                r = zk + k - 1;
                l = k;
             }
        }
        z[k] = zk;
        for(i=0;i<nt;i++){
           table[0][i+1]= {align:'center',width:25, text:i,bgColor:zcolors[i] }		
           table[1][i+1]= {align:'center',width:25, text:text.charAt(i), bgColor:tcolors[i] }
           table[2][i+1]= {align:'center',width:25, text:text.charAt(i), bgColor:pcolors[i] }
        }
        for(i=0;i<k;i++){
           table[3][i+2]= {align:'center',width:25, text:z[i+1], bgColor:''}
        }
  	    buildTable(table, div);
        sprn('Z('+k+') = '+z[k]+', l='+l+', r='+r+', '+cmp+' comparisons so far.',div);
 	    snapshots.push(div.cloneNode(true));     
    }
    sprn('_______________________________________________________________________________________',div);
    sprn('The Z algorithm has completed with '+cmp+' comparisons on a text of size '+nt+'.',div);
    table = new Array(); table[0] = new Array(); table[1] = new Array(); table[2] = new Array();
    table[0].push({text:'Index:', align:'right'});
	table[1].push({text:'Text:', align:'right'});
	table[2].push('Z values:');
    table[2].push('');
    for(i=0;i<nt;i++){
		table[0].push({text:i, align:'center'});    
        table[1][i+1]= {align:'center',width:25, text:text.charAt(i), bgColor:''}
    }
    for(i=0;i<nt-1;i++){
        table[2][i+2]= {align:'center',width:25, text:z[i+1], bgColor:''}
    }
    buildTable(table, div);
    snapshots.push(div.cloneNode(true));     
}

function setViewer(i){
	var viewer = ge('viewer');
	removeAllChildren(viewer);

	ctn('Step #' + i + ", next press 'x', previous press 'z'", ce('h2', viewer));
	viewer.appendChild(snapshots[i]);
}

function generate(text){
	ge('inputText').value = text;
    snapshots = new Array();
    animate(text);
	setViewer(sindex=0);
}

var sindex = 0;
var snapshots = new Array();
var ord = new Array(), chr = new Array();
for (var i=0; i<256; i++){
	ord[String.fromCharCode(i)] = i;
	chr[i] = String.fromCharCode(i);
}

function prevSnap(){
	if (sindex>0) setViewer(--sindex);
}

function nextSnap(){
	if (sindex+1<snapshots.length) setViewer(++sindex);
}

onkeypressListeners.push(function(e){
	var keycode = e? e.which : event.keyCode;
	switch (keycode){
		case ord['x'] : nextSnap(); break;
		case ord['z'] : prevSnap(); break;
	}
});

</script>
<h1>Z Algorithm</h1>

<p>Input your own the text and click Generate Z values to animate the Z Algorithm from Dan Gusfield's <i>Algorithms on Strings, Trees and Sequences</i> book.</p>
<p>See the <a href="https://personal.utdallas.edu/~besp/demo/John2010/z-match.htm">Z Algorithm Exact Pattern Match animation</a> for details on using Z values for pattern matching.</p>
<table>
 <tbody><tr><td>Text:</td>
    <td><input type="text" id="inputText" size="50" value="aabcaabxaaaz"></td></tr>
</tbody></table>
<input type="button" onclick="generate(ge(&#39;inputText&#39;).value)" value="Generate Z values">

<div id="viewer"><h2>Step #0, next press 'x', previous press 'z'</h2><div style="background-color: rgb(221, 221, 204); padding: 14px;"><table border="1" cellspacing="1" style="border: 0px;"><tbody><tr><td align="right">Index:</td></tr><tr><td align="right">Text:</td></tr><tr><td align="right">Text:</td></tr><tr><td>Z values:</td><td></td></tr></tbody></table><p>To illustrate the comparisons taking place, the text has been duplicated. Green boxes indicate matches, red a mismatch.</p><p>In the Z algorithm, k is the index being considered, l is the left side of the Z-box, and r is the right side of the Z-box,</p><p>The current Z box (bounded by l and r) is indicated on the Index row using pale green.</p><p>The Z algorithm starts with k=1, r=0, l=0.</p><p>Press x to generate Z values for the text using the Z Algorithm.</p></div></div>

<p>This animation was prepared for Dr. Bereg's CS 6333 Algorithms for Computational Biology class by John Yundt-Pacheco (jcy031000 _at_ utdallas.edu).<br> More information can be found at Dr. Bereg's CS 6333 site : <a href="http://www.utdallas.edu/~besp/6333">http://www.utdallas.edu/~besp/6333</a>.</p>

<p>The framework from Felix Halim's Suffix Array Demonstration was used to construct this animation: <a href="http://felix-halim.net/pg/suffix-array">http://felix-halim.net/pg/suffix-array</a>.</p>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
	</td>
</tr>
</tbody></table>
<hr>


<webchatgpt-custom-element-bedf2056-73cd-407d-ba28-ca1f3aaabe34 id="webchatgpt-snackbar" style="color: rgb(255, 255, 255);"><template shadowrootmode="open"><style id="webchatgpt-snackbar-container-emotion-style"></style><div id="webchatgpt-snackbar-container" style="z-index: 2147483647;"></div></template></webchatgpt-custom-element-bedf2056-73cd-407d-ba28-ca1f3aaabe34><yd-mg-icon style="position: fixed; z-index: 2147483647;"><template shadowrootmode="open"><style>
.item {
    display: flex;
    justify-content: center;
    align-items: center;
}
.all {
    direction: ltr;
}
.all > * {
    direction: rtl;
}
.hidden {
    display: none;
    transition: width 0.3s linear;
}
.container:hover .hidden {
    width: var(--131b0bc3);
    height: var(--367772ca);
    display: flex;
}
.container {
    position: fixed;
    top:var(--8b7ed71a);
    right:var(--2c4e45ba);
    width: var(--b2ad46e2);
    height: var(--14b33b7e);
    background-color: #fff;
    border-radius: var(--e55f886a);
    box-shadow: 0 0 10px #b3b5b8;
    transition: width 0.3s linear;
    display: flex;
    justify-content: center;
    align-items: center;
}
.container:hover {
    width: var(--301156d1);
    border-radius: var(--e55f886a);
    justify-content: space-around;
}
.icon {
    width: var(--131b0bc3);
    height: var(--367772ca);
    cursor: pointer;
    user-select: none;
}
.icon:hover {
}
.yd-translate-loader {
    border: 2px solid #f3f3f3;
    border-top: 2px solid #3498db;
    border-radius: 50%;
    height: var(--367772ca);
    width:  var(--131b0bc3);
    animation: spin 0.5s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg);
}
100% { transform: rotate(360deg);
}
}
@keyframes append-animate {
from {
        opacity: 0;
}
to {
        opacity: 1;
}
}
.popup {
  animation: popup 0.3s forwards;
}
@keyframes popup {
0% {
    opacity: 0;
    transform: scale(0);
}
100% {
    opacity: 1;
    transform: scale(1);
}
}
</style><div class="all" style="position: fixed; z-index: 2147483647; --131b0bc3: 20px; --367772ca: 20px; --8b7ed71a: 117.80000495910645px; --2c4e45ba: 270.1187324523926px; --b2ad46e2: 24px; --14b33b7e: 24px; --e55f886a: 12px; --301156d1: 72px;"><!----></div></template></yd-mg-icon><yd-mg-block-icon><template shadowrootmode="open"><style>@charset "UTF-8";
.disabled-element[data-v-d3135d60] {
  cursor: not-allowed;
}
.all[data-v-d3135d60] {
  position: absolute;
  z-index: 1;
}
.all .container[data-v-d3135d60] {
  width: var(--5a3ced10);
  height: var(--58dc89c6);
  display: flex;
  justify-content: center;
  align-items: center;
}
.all .container .yd-line[data-v-d3135d60] {
  display: block;
  position: absolute;
  height: var(--c03ec7f0);
  top: var(--7808ebd8);
  left: var(--891c21b0);
}
.all .container .yd-line .orignal[data-v-d3135d60] {
  padding: 1px;
  background-color: #E4E7F3;
  height: var(--5934d8d6);
}
.all .container .yd-line .yd[data-v-d3135d60] {
  padding: 1px;
  background-color: #FF939E;
  height: var(--4d327ada);
}
.all .container .yd-line .llm[data-v-d3135d60] {
  padding: 1px;
  background-color: #2485FF;
  height: var(--18678bd3);
}
.all .container .tooltip-container[data-v-d3135d60] {
  display: inline-block;
  position: absolute;
  top: var(--147b79a2);
  left: var(--7afb4d26);
}
.all .container .tooltip-container .icon[data-v-d3135d60] {
  width: var(--5a3ced10);
  height: var(--58dc89c6);
  background-image: var(--4a440a69);
  border-radius: 5px;
  cursor: pointer;
}
.all .container .tooltip-container .icon[data-v-d3135d60]::before {
  content: var(--4809e853);
  display: none;
}
.all .container .tooltip-container .icon[data-v-d3135d60]:hover {
  box-shadow: 0px 4px 10px rgba(56, 112, 200, 0.16);
  transition: width 0.3s linear;
  background-image: var(--4809e853);
  cursor: pointer;
}
.all .container .tooltip-container .llmIcon[data-v-d3135d60] {
  width: var(--5a3ced10);
  height: var(--58dc89c6);
  background-image: var(--4a440a69);
  border-radius: 5px;
}
.all .container .tooltip-container .llmIcon[data-v-d3135d60]:hover {
  width: var(--5a3ced10);
  height: 48px;
  background-image: var(--11da4116);
  border-radius: 5px;
}
.all .container .tooltip-container .tooltip[data-v-d3135d60] {
  position: absolute;
  top: var(--523390c5);
  /* &#25918;&#22312;&#20803;&#32032;&#30340;&#19978;&#26041; */
  left: var(--178a82d2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background-color: #F9FBFF;
  padding: 8px;
  width: 150px;
  height: 40px;
  border-radius: 8px;
  box-sizing: border-box;
  border: 1px solid rgba(216, 217, 219, 0.5);
  box-shadow: 0px 1.2px 3.6px 0px rgba(0, 0, 0, 0.1), 0px 2px 20px 0px rgba(27, 19, 98, 0.08);
  font-family: PingFang SC;
  font-size: 16px;
  font-weight: normal;
  line-height: 150%;
  text-align: center;
  letter-spacing: 0em;
  user-select: none;
  color: #2A2B2E;
}
.all .container .tooltip-container .llmTooltip[data-v-d3135d60] {
  position: absolute;
  top: var(--523390c5);
  /* &#25918;&#22312;&#20803;&#32032;&#30340;&#19978;&#26041; */
  left: var(--178a82d2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background-color: #F9FBFF;
  padding: 8px;
  width: 200px;
  height: 40px;
  border-radius: 8px;
  box-sizing: border-box;
  border: 1px solid rgba(216, 217, 219, 0.5);
  box-shadow: 0px 1.2px 3.6px 0px rgba(0, 0, 0, 0.1), 0px 2px 20px 0px rgba(27, 19, 98, 0.08);
  font-family: PingFang SC;
  font-size: 16px;
  font-weight: normal;
  line-height: 150%;
  text-align: center;
  letter-spacing: 0em;
  user-select: none;
  color: #2A2B2E;
}
.all .container .yd-translate-loader-block[data-v-d3135d60] {
  position: absolute;
  top: var(--147b79a2);
  left: var(--7afb4d26);
  width: var(--5a3ced10);
  height: var(--5a3ced10);
  background-image: var(--3fa19693);
  /* &#30830;&#20445;&#36825;&#37324;&#26159;&#27491;&#30830;&#30340;&#22270;&#29255;&#36335;&#24452; */
  background-repeat: no-repeat;
  background-size: cover;
  box-shadow: 0px 4px 10px rgba(56, 112, 200, 0.16);
  overflow: hidden;
  /* &#20445;&#25345;&#23376;&#20803;&#32032;&#30340;&#22278;&#35282;&#25928;&#26524; */
}
.all .container .yd-translate-loader-block[data-v-d3135d60]::before {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  border-radius: 5px;
  border: 1px solid transparent;
  background: linear-gradient(to left bottom, rgba(38, 132, 255, 0.6), rgba(120, 85, 250, 0.6));
  -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}
.all .container .tip[data-v-d3135d60] {
  background-color: #F9FBFF;
  padding: 8px;
  border-radius: 8px;
  height: 400px;
  border: 1px solid #D8D9DB;
}</style><div data-v-d3135d60="" class="all notranslate" style="--5a3ced10: 24px; --58dc89c6: 24px; --c03ec7f0: 0px; --7808ebd8: 0px; --891c21b0: 0px; --5934d8d6: 0px; --4d327ada: 0px; --18678bd3: 0px; --147b79a2: -16px; --7afb4d26: -44px; --4a440a69: url(chrome-extension://memhacajcfhmibggbgilihlmiiddeggo/block.svg); --4809e853: url(chrome-extension://memhacajcfhmibggbgilihlmiiddeggo/block-h.svg); --11da4116: url(chrome-extension://memhacajcfhmibggbgilihlmiiddeggo/block-h-llm.svg); --523390c5: undefined; --178a82d2: undefined; --3fa19693: url(chrome-extension://memhacajcfhmibggbgilihlmiiddeggo/block-l.gif);"><!----></div></template></yd-mg-block-icon><yd-image-ocr style="z-index: 2147483647;"><template shadowrootmode="open"><style>@charset "UTF-8";
.modal-overlay[data-v-04ce0a75] {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
}
.modal-content[data-v-04ce0a75] {
  background-color: white;
  padding: 20px;
  border-radius: 10px;
  display: flex;
  gap: 20px;
  width: 800px;
  height: 500px;
  flex-direction: column;
  position: relative;
}
.modal-header[data-v-04ce0a75] {
  display: flex;
  justify-content: flex-end;
  height: 24px;
}
.modal-body[data-v-04ce0a75] {
  display: flex;
  flex-grow: 1;
  flex-direction: row;
}
.modal-body .imageContainer[data-v-04ce0a75] {
  flex: 2;
  justify-content: center;
}
.modal-body .img[data-v-04ce0a75] {
  position: relative;
  background-size: contain;
  background-position: center;
  height: 600px;
  background-repeat: no-repeat;
}
.modal-body .img .box[data-v-04ce0a75] {
  position: absolute;
  border: #333 3px solid;
}
.modal-body .text-content[data-v-04ce0a75] {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /* &#26681;&#25454;&#38656;&#35201;&#35843;&#25972;&#25991;&#26412;&#20869;&#23481;&#30340;&#26679;&#24335; */
}
.modal-body .text-content .text[data-v-04ce0a75] {
  overflow-y: auto;
  height: 450px;
}
.modal-body .text-content .toolbar[data-v-04ce0a75] {
  height: 36px;
  display: flex;
  justify-content: space-around;
  flex-direction: row;
  flex-wrap: wrap;
}
.close-button[data-v-04ce0a75] {
  background-color: transparent;
  /* &#36879;&#26126;&#32972;&#26223; */
  border: none;
  /* &#21435;&#38500;&#36793;&#26694; */
  cursor: pointer;
  /* &#40736;&#26631;&#24748;&#20572;&#26102;&#26174;&#31034;&#25351;&#38024; */
  outline: none;
  /* &#21435;&#38500;&#28966;&#28857;&#36718;&#24275; */
  position: absolute;
  /* &#32477;&#23545;&#23450;&#20301; */
  top: 10px;
  /* &#36317;&#39030;&#37096;&#30340;&#36317;&#31163; */
  right: 10px;
  /* &#36317;&#21491;&#36793;&#30340;&#36317;&#31163; */
  font-size: 24px;
  /* &#23383;&#20307;&#22823;&#23567; */
  line-height: 24px;
  /* &#34892;&#39640;&#65292;&#20197;&#30830;&#20445;&#22402;&#30452;&#23621;&#20013; */
  color: #333;
  /* &#23383;&#20307;&#39068;&#33394; */
  font-weight: bold;
  /* &#23383;&#20307;&#21152;&#31895; */
}
.close-button[data-v-04ce0a75]:hover {
  color: #666;
  /* &#40736;&#26631;&#24748;&#20572;&#26102;&#30340;&#39068;&#33394;&#21464;&#21270; */
}
.line[data-v-04ce0a75], .redline[data-v-04ce0a75] {
  font-family: Arial, Helvetica, sans-serif;
  /* &#20351;&#29992;&#26080;&#34924;&#32447;&#23383;&#20307; */
  font-size: 16px;
  /* &#35774;&#32622;&#21512;&#36866;&#30340;&#23383;&#20307;&#22823;&#23567; */
  line-height: 24px;
  color: #333;
  /* &#25991;&#23383;&#39068;&#33394; */
  padding: 3px 3px 3px 6px;
  /* &#20869;&#36793;&#36317; */
  margin-top: 5px;
  cursor: pointer;
  transition: background-color 0.3s, border-left-color 0.3s;
  /* &#32972;&#26223;&#33394;&#21644;&#36793;&#26694;&#39068;&#33394;&#21464;&#21270;&#30340;&#36807;&#28193;&#25928;&#26524; */
  border-left: 3px solid #ddd;
  /* &#35774;&#32622;&#28784;&#33394;&#30340;&#24038;&#36793;&#26694; */
}
.redline[data-v-04ce0a75] {
  border-left-color: #e53935;
  /* &#40736;&#26631;&#24748;&#20572;&#26102;&#36793;&#26694;&#39068;&#33394;&#21464;&#20026;&#32418;&#33394; */
  background-color: #f5f5f5;
}
.button[data-v-04ce0a75] {
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
  background-color: #FB4A3E;
  /* &#19968;&#20010;&#26126;&#20142;&#20294;&#19981;&#39281;&#21644;&#30340;&#32418;&#33394; */
  color: white;
  /* &#25991;&#26412;&#39068;&#33394;&#20026;&#30333;&#33394; */
  padding: 5px 10px;
  /* &#25353;&#38062;&#20869;&#36793;&#36317; */
  border: none;
  /* &#26080;&#36793;&#26694; */
  border-radius: 5px;
  /* &#36731;&#24494;&#30340;&#22278;&#35282; */
  font-size: 10px;
  text-transform: uppercase;
  /* &#25991;&#26412;&#22823;&#20889; */
  cursor: pointer;
  /* &#40736;&#26631;&#24748;&#20572;&#26102;&#30340;&#25351;&#38024;&#26679;&#24335; */
  transition: background-color 0.3s;
  /* &#32972;&#26223;&#39068;&#33394;&#21464;&#21270;&#30340;&#36807;&#28193;&#25928;&#26524; */
}
.button[data-v-04ce0a75]:hover {
  background-color: #d32f2f;
  /* &#40736;&#26631;&#24748;&#20572;&#26102;&#30340;&#32972;&#26223;&#39068;&#33394;&#31245;&#26263; */
}
.button[data-v-04ce0a75]:active {
  background-color: #c62828;
  /* &#40736;&#26631;&#28857;&#20987;&#26102;&#30340;&#32972;&#26223;&#39068;&#33394;&#26356;&#26263; */
}
.button[data-v-04ce0a75]:disabled {
  background-color: #ef9a9a;
  /* &#31105;&#29992;&#29366;&#24577;&#30340;&#25353;&#38062;&#39068;&#33394;&#26356;&#20142;&#65292;&#26356;&#23569;&#39281;&#21644;&#24230; */
  cursor: default;
  /* &#31105;&#29992;&#29366;&#24577;&#30340;&#40736;&#26631;&#26679;&#24335; */
}
.loading-indicator[data-v-04ce0a75] {
  /* &#28155;&#21152;&#20320;&#30340;&#26679;&#24335;&#65292;&#27604;&#22914;&#23621;&#20013;&#26174;&#31034;&#12289;&#21160;&#30011;&#31561; */
  position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 1.5em;
  /* ...&#20854;&#20182;&#26679;&#24335; */
}
.message[data-v-04ce0a75] {
  position: absolute;
  top: 50%;
  /* &#23450;&#20301;&#21040;&#29238;&#20803;&#32032;&#30340;&#20013;&#38388; */
  left: 50%;
  /* &#23450;&#20301;&#21040;&#29238;&#20803;&#32032;&#30340;&#20013;&#38388; */
  transform: translate(-50%, -50%);
  /* &#20351;&#29992; transform &#23454;&#29616;&#31934;&#30830;&#23621;&#20013; */
  padding: 10px;
  background-color: white;
  /* &#35774;&#32622;&#32972;&#26223;&#39068;&#33394;&#20026;&#30333;&#33394; */
  border: 1px solid blue;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  /* &#28155;&#21152;&#38452;&#24433;&#25928;&#26524; */
  text-align: center;
  z-index: 100;
  /* &#30830;&#20445;&#25552;&#31034;&#20449;&#24687;&#22312;&#20854;&#20182;&#20803;&#32032;&#19978;&#26041; */
}
.loader[data-v-04ce0a75] {
  border: 5px solid #f3f3f3;
  /* &#27973;&#28784;&#33394;&#36793;&#26694; */
  border-top: 5px solid #3498db;
  /* &#34013;&#33394;&#36793;&#26694; */
  border-radius: 50%;
  width: 40px;
  height: 40px;
  animation: spin-04ce0a75 2s linear infinite;
  margin-bottom: 20px;
}
@keyframes spin-04ce0a75 {
0% {
    transform: rotate(0deg);
}
100% {
    transform: rotate(360deg);
}
}</style><!----></template></yd-image-ocr></body></html>
posted @ 2024-07-18 19:54  GuTongXing  阅读(101)  评论(0)    收藏  举报